Cum să construiți un plugin Sketch cu JavaScript, HTML și CSS (Partea 1)

Publicat: 2022-03-10
Rezumat rapid ↬ Dacă ați lucrat vreodată cu Sketch, șansele sunt că au existat o mulțime de momente în care v-ați gândit: „Dacă Sketch ar putea face acest lucru anume, aș fi capabil să îndeplinesc sarcina pe care o aveam. mult mai rapid, mai ușor și mai bine.” Ei bine, nu te mai îngrijora! În acest articol din două părți, veți învăța cum să vă construiți propriile pluginuri Sketch de la zero, oferindu-vă abilitățile necesare pentru a rezolva exact acest tip de probleme.

Acest tutorial este destinat persoanelor care cunosc și folosesc aplicația Sketch și nu se tem să se amestece cu codul. Pentru a profita cel mai mult de pe urma, va trebui să aveți cel puțin o experiență de bază în scrierea JavaScript (și, opțional, HTML/CSS).

Pluginul pe care îl vom crea se numește „Mozaic”. În prima parte, vom afla despre fișierele de bază care alcătuiesc un plugin Sketch; vom scrie ceva JavaScript și vom crea o interfață de utilizator pentru pluginul nostru cu ajutorul unor coduri HTML și CSS. Următorul articol va fi despre cum să conectați interfața cu utilizatorul la codul principal al pluginului, despre cum să implementați principalele caracteristici ale pluginului, iar la sfârșitul acestuia, veți învăța, de asemenea, cum să optimizați codul și modul în care funcționează pluginul.

De asemenea, voi distribui codul pluginului (JS, HTML, CSS) și fișierele pe care le veți putea examina și utiliza în scopuri de învățare.

Ce sunt pluginurile Sketch și cum funcționează?

În Sketch, pluginurile sunt o modalitate de a adăuga caracteristici și funcționalități care nu sunt prezente în Sketch „din cutie”. Având în vedere că aproape întotdeauna va lipsi o caracteristică sau o integrare în orice program dat (mai ales având în vedere numărul mare de nevoi pe care le-ar avea orice designer individual!), ne putem imagina cum pluginurile ar putea fi deosebit de utile și puternice. Pluginurile Sketch sunt capabile să facă aproape tot ceea ce v-ați aștepta, cum ar fi manipularea culorii, formei, mărimii, ordinii, stilului, grupării și efectelor straturilor, dar și pot face lucruri precum trimiterea de solicitări către resursele de internet, prezentarea unui utilizator interfață și multe, multe altele!

Pe partea de programare, toate pluginurile Sketch sunt scrise în cod JavaScript. Ei bine, de fapt, asta nu este în întregime adevărat. Este mai corect să spunem că majoritatea pluginurilor Sketch sunt scrise în JavaScript, deoarece este, de asemenea, posibil să scrieți un plugin Sketch într-unul dintre limbajele de programare Apple, Objective-C și Swift, deși chiar și acestea necesită o cantitate mică de cunoștințe JavaScript.

Nu-ți face griji totuși. În acest articol, ne vom concentra asupra modului de a construi pluginuri Sketch folosind numai JavaScript, HTML și CSS . Nu vom trece peste elementele de bază ale HTML, CSS sau JavaScript - acest articol presupune cel puțin cunoștințe și experiență cu toate aceste trei. Site-ul web pentru dezvoltatori MDN oferă un loc minunat pentru a afla mai multe despre dezvoltarea web.

Mai multe după săritură! Continuați să citiți mai jos ↓

Să începem!

În primul rând, ce facem?

În acest tutorial, vă voi învăța cum să construiți un plugin de bază, prietenos pentru începători, care va putea crea, duplica și modifica straturi, precum și să prezinte utilizatorului o interfață de utilizator plăcută. Procedând astfel, scopul meu este să stabilesc cunoștințele fundamentale pe care să le puteți construi și să le utilizați pentru a vă crea propriile plugin-uri.

Pluginul pe care îl vom construi se numește Mosaic și este efectiv un „generator de modele”. Alimentați-l cu straturile, modificați câteva setări și va crea un model:

Imagine care arată interfața de utilizare a pluginului Mosaic și câteva exemple de modele.
Interfața de utilizare a mozaicului și câteva exemple de modele realizate cu acesta. (Previzualizare mare)

Dacă doriți să instalați și să vă jucați cu Mosaic, puteți descărca pluginul completat de pe GitHub.

Un pic de istorie: Mosaic este inspirat în mare parte de un plugin vechi Adobe Fireworks numit Twist-and-Fade . Twist-and-Fade a fost destul de puternic, capabil să dubleze un strat de orice număr de ori în timp ce îi ajusta nuanța, poziția, rotația, dimensiunea și opacitatea. Pluginul a reușit chiar să genereze GIF-uri animate, ca acesta, unde a creat cadrele pentru cele două elemente rotative din caseta:

Imagine care arată o casetă muzicală cu tobe rotative
Casetă animată (sursă). (Previzualizare mare)

(Iată un videoclip care arată Twist and Fade dacă sunteți interesat să vedeți exact cum a funcționat.)

În scopul acestui tutorial, vom construi un plugin oarecum similar pentru Sketch, deși simplificat în mod intenționat, astfel încât să păstrăm tutorialul cât mai accesibil posibil. Mai exact, pluginul nostru va putea:

  • Duplicați orice strat Sketch (bitmap sau vector) și modificați poziția, rotația și opacitatea stratului duplicat. Acest lucru ne va oferi o introducere în manipularea straturilor folosind API-urile JavaScript ale Sketch.
  • Afișați o interfață de utilizator creată folosind HTML, CSS și JS, care vă va învăța cum să creați cu ușurință o interfață pentru plugin, utilizând tehnologii web cu care este posibil să fiți deja familiarizat. Interfața pluginului este destul de importantă, deoarece este modul în care vom aduna intrările utilizatorului cu privire la modul în care utilizatorul dorește să arate imaginea mozaic rezultată.

Crearea pluginului nostru de bază în zece secunde

În primul rând, vom crea „baza” (sau șablonul) pentru pluginul pe care dorim să îl construim. Am putea crea manual toate fișierele și folderele necesare care alcătuiesc un plugin, dar, din fericire, nu trebuie, deoarece Sketch o poate face pentru noi. După ce am generat pluginul șablon, îl vom putea personaliza după cum credem de cuviință.

Există o tehnică foarte rapidă și ușoară pe care o putem folosi pentru a crea pluginul șablon, care este aproape metoda mea de bază atunci când trebuie să împletesc un plugin pentru a rezolva orice problemă cu care mă confrunt la un moment dat. Iată cum funcționează:

Cu Sketch deschis, verificați bara de meniu din partea de sus a ecranului și faceți clic pe Plugins -> Run Script . Aceasta va deschide o casetă de dialog pe care o putem folosi pentru a testa și a rula codul. De asemenea, putem salva orice cod pe care îl introducem ca plugin, care este partea de care suntem interesați în mod special în acest moment.

Ștergeți orice cod care se află deja în acest dialog și înlocuiți-l cu următorul cod demonstrativ:

 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!");

Apoi, apăsați Save Script as Plugin în partea din stânga jos a ferestrei, introduceți orice nume doriți ca acest plugin să aibă (în cazul nostru, acesta este „Mozaic”), apoi Save Script as Plugin .

Apăsați „Salvați” în partea din stânga jos a ferestrei și introduceți orice nume doriți ca acest plugin să aibă. (Previzualizare mare)

Credeți sau nu, am terminat deja - tot ce rămâne este să mâncați tortul pe care tocmai l-am coapt. Aici vine partea distractivă. Deschizând din nou meniul Plugin-uri, ar trebui să vedeți ceva de genul acesta: plugin-ul dvs. nou-nouț, listat ca „Mozaic”! Apasa pe el!

(Previzualizare mare)

Felicitări, tocmai ai scris primul tău plugin Sketch!

Ceea ce ar trebui să vedeți după ce faceți clic pe „Mozaic” ar trebui să fie ca în videoclipul scurt de mai sus, cu un mesaj discret care apare în partea de jos a ecranului, care începe cu cuvintele „Hei, acolo...” – exact ceea ce îi spune codul pe care l-am lipit. a face. Acesta este ceea ce face această tehnică atât de grozavă: facilitează lipirea, modificarea și testarea codului fără a fi nevoie să construiți un plugin de la zero. Dacă sunteți familiarizat cu sau v-ați jucat vreodată cu consola web a browserului dvs., aceasta este practic asta. A avea acest instrument în buzunarul din spate în timp ce construiți și testați codul este o necesitate.

Să facem o scurtă descriere a ceea ce face codul adăugat:

Mai întâi, importă modulul sketch/ui al bibliotecii JS încorporate din Sketch și îl atribuie variabilei UI . Acest modul conține câteva metode utile legate de interfață, dintre care una o vom folosi:

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

Apoi, apelează metoda message (care face parte din modulul sketch/ui ) cu șirul de text pe care îl dorim să fie afișat în sfatul cu instrumente pe care l-am văzut:

 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!");

Metoda message() oferă o modalitate excelentă de a prezenta utilizatorului un mesaj discret; este grozav pentru cazurile în care nu trebuie să furați focalizarea (non-modal) și nu aveți nevoie de butoane sau câmpuri de text sofisticate. Există și alte modalități de a prezenta elemente comune ale interfeței de utilizare, cum ar fi alerte, solicitări și altele, unele dintre ele pe care le vom folosi pe măsură ce construim Mosaic.

Personalizarea metadatelor pluginului nostru

Avem acum un plugin de bază de la care să începem, dar încă trebuie să-l modificăm în continuare și să-l facem cu adevărat al nostru. Următorul nostru pas va fi să schimbăm metadatele pluginului.

Pentru acest pas, va trebui să aruncăm o privire în ceea ce se numește pachetul de pluginuri . Când apăsați pe Salvare în fereastra „Run Script”, Sketch a salvat pluginul dvs. ca un folder numit Mosaic.sketchplugin , pe care îl puteți găsi în directorul ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins . E cam lung și enervant de reținut; ca o comandă rapidă, o puteți ridica și prin Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder . Chiar dacă apare în Finder ca un singur fișier, este de fapt un folder care conține tot ce are nevoie de pluginul nostru pentru ca Sketch să-l ruleze. Motivul pentru care apare ca un singur fișier, în ciuda faptului că este un folder, este pentru că atunci când ați instalat pentru prima dată Sketch, Sketch a înregistrat extensia .sketchplugin ca un „pachet” (un tip special de folder care apare ca fișier) și a cerut să se deschidă automat. în Schiță când este deschis.

Să aruncăm o privire înăuntru. Faceți clic dreapta pe Mosaic.sketchplugin , apoi faceți clic pe „Afișați conținutul pachetului”. În interior, ar trebui să vedeți următoarea structură de directoare:

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

S-ar putea să vă întrebați de ce există un fișier acolo cu extensia .cocoascript . Nu vă faceți griji - este doar un fișier JavaScript obișnuit și conține doar codul pe care l-am introdus mai devreme. Continuați și redenumiți acest fișier în index.js , care va schimba structura directorului pentru a arăta ca cea de mai jos:

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

Cel mai obișnuit mod de organizare a fișierelor într-un pachet de pluginuri este următorul: codul dvs. (JavaScript) și manifest.json aparțin în Sketch/ , iar resursele (gândiți-vă că imagini, fișiere audio, fișiere text etc.) aparțin în Resources/ .

Să începem prin a ajusta fișierul numit manifest.json . Deschideți-l în editorul de cod preferat, cum ar fi Visual Studio Code sau Atom.

Veți vedea că în acest moment este relativ puțin înăuntru aici, dar vom adăuga mai multe în curând. Manifestul pluginului servește în primul rând două scopuri:

  1. În primul rând, furnizează metadate care descriu pluginul utilizatorului - lucruri precum numele, versiunea, numele autorului și așa mai departe. Sketch folosește aceste informații în dialogul Sketch -> Preferences -> Plugins pentru a crea o listă și o descriere pentru pluginul dvs.
  2. În al doilea rând, îi spune lui Sketch despre cum să vă ocupați de afacerea dvs.; adică, îi spune lui Sketch cum doriți să arate meniul pluginului dvs., ce taste rapide să atribuiți pluginului dvs. și unde se află codul pluginului dvs. (pentru ca Sketch să îl poată rula).

Având în vedere scopul #1, descrierea pluginului pentru utilizator, probabil veți observa că în acest moment nu există nicio descriere sau autor, ceea ce ar fi confuz pentru utilizator și ar face pluginul dificil de identificat. Să rezolvăm asta ajustând valorile cheilor relevante la:

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

În continuare, să ajustăm identificatorul pluginului. Acest identificator folosește ceea ce se numește „notație inversă a domeniului”, care este un mod cu adevărat concis (sau plictisitor, alegeți) de a spune „luați domeniul site-ului dvs., inversați ordinea, apoi puneți numele produsului la sfârșit”. Acesta va ieși ceva de genul: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct .

Nu trebuie să respectați această convenție de denumire - puteți pune tot ce doriți aici, atâta timp cât este suficient de unic pentru a evita conflictele cu alte plugin-uri (deși probabil că este o idee bună să rămâneți la formatul RDN, mai ales că oferă un sistem simplu, reutilizabil pentru identificatorii dvs. de plugin).

În acest sens, schimbați-vă identificatorul în com.your-name.mosaic :

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

Îmi place personal să iau toate cheile legate de metadate (titlu, autor, identificator etc.) și să le grupez în partea de sus a manifestului, astfel încât să nu fie răspândite peste tot și să-mi ajute să-mi păstrez sănătatea mentală atunci când trebuie să le găsesc .

În continuare, să aruncăm o privire la menu și tastele de commands . Acești doi sunt responsabili pentru a-i spune lui Sketch ce cod să apeleze și ca răspuns la ce.

Dacă te uiți la cheia de menu , vei vedea că conține o cheie de title , a cărei valoare este numele cu care va apărea pluginul nostru în meniul Plugins . Are, de asemenea, o cheie de items , care este o listă de identificatori de comandă :

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

În acest moment, există un singur identificator de comandă în această listă, "com.bohemiancoding.sketch.runscriptidentifier" . Identificatorii de comandă indică întotdeauna o comandă din lista de commands . În acest moment, pluginul nostru are o singură comandă, care este cea cu acest identificator:

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

Ori de câte ori adăugați un identificator de comandă la o intrare de menu , Sketch va căuta intrarea de comandă care are acel identificator și va afișa valoarea cheii de name (care în acest caz este „Mozaic”) și o va afișa în meniul pluginului dvs. a identificatorului.

În ceea ce privește rolul pe care îl joacă comenzile, ne putem gândi la o intrare de comandă ca o modalitate de a-i spune lui Sketch ce funcție din codul JavaScript al pluginului nostru dorim să rulăm atunci când acea comandă este invocată, „invocarea” fiind de obicei clicul utilizatorului pe meniul asociat. articol. Intrarea de comandă nu face nimic singură, este doar JSON - pur și simplu oferă o descriere pentru Sketch despre unde să caute JavaScript-ul de care trebuie să ruleze atunci când comanda este invocată.

Până acum, am vorbit despre ceea ce fac name unei comenzi și cheile de identifier , dar există alte două chei într-o comandă care trebuie abordate: script și handlers .

Cheia de script îi spune lui Sketch unde se află fișierul JavaScript pe care ar trebui să-l ruleze. Observați cum Sketch presupune că fișierul script în cauză se află în folderul Sketch/ , motiv pentru care, din motive de simplitate, veți dori să vă asigurați că tot codul JavaScript se află undeva sub folderul Sketch/ . Înainte de a trece de la această cheie , este important să vă asigurați că modificați valoarea acestei chei în index.js , la fel cum am redenumit fișierul mai devreme. În caz contrar, Sketch nu va putea găsi și rula fișierul JavaScript.

Valoarea cheii de handlers este ceea ce se uită Sketch pentru a determina ce funcție din JavaScript să apeleze. Aici, avem un singur set de handler: run , cu valoarea onRun . run este numele unei acțiuni Sketch predefinite, încorporate. Această acțiune run va fi apelată întotdeauna când un utilizator face clic pe un element de meniu care face referire la această comandă. onRun este numele unei funcții din fișierul script.cocoascript generat automat (pe care l-am redenumit în index.js ) și funcția pe care dorim să o apelăm atunci când are loc evenimentul de run , adică atunci când utilizatorul face clic pe elementul de meniu.

În exemplul pe care îl avem până acum, acest proces se desfășoară cam așa:

  1. Utilizatorul face clic pe elementul nostru de meniu.
  2. Sketch găsește comanda asociată cu acel element de meniu.
  3. Sketch găsește fișierul script la care se referă comanda și îl rulează (ceea ce în acest caz înseamnă că execută JavaScript în index.js ).
  4. Deoarece această comandă a fost invocată de un clic pe un element de meniu, este considerată o acțiune de run . Aceasta înseamnă că Sketch se va uita la valoarea handlers.run a comenzii pentru funcția de apelată în continuare, care în acest caz este onRun .
  5. Sketch apelează funcția onRun .

Comenzile sunt apelate cel mai frecvent ca răspuns la un utilizator care face clic pe unul dintre elementele dvs. de meniu, dar pot fi apelate și ca răspuns la alte acțiuni ale utilizatorului, cum ar fi schimbarea de către utilizator a selecției sau o proprietate pe un strat. Cu toate acestea, pentru acest plugin, nu vom folosi niciuna dintre aceste alte acțiuni. (Puteți afla mai multe despre acțiuni și despre cum funcționează acestea în pagina de ajutor Action API.)

Înainte de a trece de la acest manifest, vom dori să facem alte două modificări. Momentan, meniul nostru are structura:

 Mosaic └ Mosaic 
Imagine care arată elementul de meniu Mozaic imbricat redundant în interiorul altui meniu numit Mozaic
Destul de redundant, nu? (Previzualizare mare)

… ceea ce este puțin redundant, deoarece pluginul nostru are un singur element de meniu. De asemenea, adaugă un pic de frecare inutilă pentru utilizatorul nostru, deoarece pluginul nostru acum necesită două clicuri pentru a invoca, mai degrabă decât unul. Putem rezolva acest lucru adăugând isRoot: true în menu nostru:

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

Acest lucru îi spune lui Sketch să plaseze primul nivel de elemente de meniu direct sub meniul Plugins -uri, mai degrabă decât să le așeze sub title meniului .

Apăsați pe Salvare și reveniți la Sketch. Ar trebui să vedeți că acum Mosaic -> Mosaic a fost înlocuit doar cu Mosaic - perfect!

Imagine care arată interfața de utilizare a pluginului Mosaic
Interfața de utilizare a lui Mosaic. (Previzualizare mare)

În ceea ce privește a doua noastră modificare, haideți să redenumim acest identificator de comandă în ceva mai puțin greoi. Deoarece identificatorii de comandă trebuie să fie unici doar în contextul unui plugin individual, îl putem redenumi în siguranță la ceva mai concis și mai evident, cum ar fi "open" :

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

Înainte de a trece mai departe, este util să rețineți că meniurile pot conține și alte meniuri. Puteți crea cu ușurință un submeniu prin imbricarea unei alte { title: ..., items: ... } în lista de items din alt meniu:

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

Construirea interfeței de utilizator a pluginului

Până acum, am scris un cod demonstrativ și am personalizat manifestul pluginului nostru. Vom trece acum la crearea interfeței sale de utilizator, care este în esență o pagină web încorporată într-o fereastră (în mod asemănător cu browserele cu care ești familiarizat):

Fereastra pluginului. (Previzualizare mare)
Imagine care arată componentele care compun interfața pluginului nostru: fereastră și vizualizare web
Componentele care compun pluginul nostru. (Previzualizare mare)

Fereastra

Designul interfeței cu utilizatorul Mosaic are propria sa fereastră, pe care o putem considera cea mai de bază componentă; vom începe cu el. Pentru a crea și afișa o fereastră, va trebui să folosim o clasă care este încorporată implicit în macOS, numită NSWindow . În restul acestui tutorial, vom face acest lucru destul de puțin (folosind API-uri încorporate precum NSWindow ), care ar putea părea puțin descurajantă dacă nu sunteți familiarizat cu el, dar nu vă faceți griji - vă voi explica totul pe parcurs!

Notă: În timp ce vorbim despre API-uri încorporate, motivul pentru care putem folosi această clasă este datorită unui pod prezent în timpul de execuție JavaScript utilizat de pluginurile Sketch. Această punte importă automat aceste clase, metode și funcții încorporate care ar fi în mod normal disponibile numai pentru aplicațiile native.

Deschideți Sketch/index.js în editorul de cod, ștergeți ceea ce există deja și inserați următoarele:

 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); };

Să aruncăm o privire la ceea ce face acest prim fragment de cod:

 function onRun(context){

Vă amintiți mai devreme când am vorbit despre comenzi și despre cum funcționează acestea și i-am spus lui Sketch să apeleze ca răspuns la un clic pe meniu numit onRun ? (Dacă aveți nevoie de o reîmprospătare, revedeți partea de mai sus, apoi reveniți.) Tot ceea ce face acest bit este să creeze acea funcție. Veți observa, de asemenea, funcția noastră onRun are un argument de context . Acesta este un argument cu care Sketch va apela manevrele dvs. de comandă, care ne poate oferi anumite informații. Mai târziu, îl vom folosi pentru a obține adresa URL a pachetului nostru de pluginuri pe computerul utilizatorului.

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

Aici, de fapt, facem câteva lucruri:

  1. În primul rând, apelăm alloc() pe NSWindow ; aceasta înseamnă, practic, „lasă deoparte o memorie pentru o instanță de NSWindow”. Este suficient să știți că va trebui să faceți acest lucru pentru fiecare instanță a unei clase native pe care doriți să o creați. Metoda alloc este disponibilă în fiecare clasă nativă.
  2. Apoi, numim metoda de inițializare a lui NSWindow (adică metoda care creează de fapt o instanță a NSWindow ), care este numită initWithContentRect:styleMask:backing:defer: . Veți observa că este diferit de ceea ce numim în codul nostru de mai sus - are o grămadă de două puncte ( : ) între fiecare argument. Deoarece nu putem folosi acea sintaxă în JavaScript, Sketch o redenumește în mod convenabil în ceva pe care îl putem folosi de fapt prin înlocuirea punctelor cu caractere de subliniere, așa cum obținem numele său JS: initWithContentRect_styleMask_backing_defer .
  3. În continuare, trecem în fiecare dintre argumentele de care are nevoie metoda. Pentru primul argument, contentRect , furnizăm un dreptunghi cu o dimensiune suficient de mare pentru interfața noastră cu utilizatorul.
  4. Pentru styleMask , folosim o mască de bit care spune că dorim ca fereastra noastră să aibă un buton de închidere, o bară de titlu și să fie redimensionabilă.
  5. Următoarele două argumente, backing și defer , vor fi întotdeauna setate la NSBackingStoreBuffered și false , așa că nu trebuie să ne facem griji pentru ele. (Documentația pentru această metodă intră în mai multe detalii despre motivul pentru care se întâmplă acest lucru.)
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

Aici setăm proprietatea releasedWhenClosed a lui NSWindow la false , ceea ce înseamnă: „Hei! nu ștergeți această fereastră din memorie doar pentru că utilizatorul o închide.” Apoi numim makeKeyAndOrderFront (null) , ceea ce înseamnă: „Mutați această fereastră în prim-plan și acordați-i focalizarea tastaturii.”

Vizualizare Web: Interfața

Pentru a ușura lucrurile, am scris deja codul HTML și CSS al interfeței de utilizator web a pluginului pe care o vom folosi; singurul cod rămas pe care va trebui să-l adăugăm se va ocupa de a ne asigura că putem comunica între acesta și codul pluginului nostru Sketch.

Apoi, descărcați codul HTML și CSS. După ce l-ați descărcat, extrageți-l, apoi mutați folderul numit „web-ui” în folderul Resurse al pluginului nostru.

Notă : Scrierea și optimizarea codului HTML/CSS real este în afara domeniului de aplicare al acestui tutorial, deoarece se concentrează pe JavaScript, care alimentează caracteristicile de bază ale pluginului; dar există o mulțime de tutoriale pe web pe acest subiect, dacă doriți să aflați mai multe.

Dacă rulați pluginul nostru acum, veți vedea că arată o fereastră - da, progres! Dar este gol, fără titlu și încă nu este deosebit de util. Trebuie să-l facem să ne arate interfața web. Pentru a face asta, va trebui să folosim o altă clasă nativă, WKWebView , care este o vizualizare creată special pentru afișarea conținutului web.

Vom adăuga codul necesar pentru a crea WKWebView sub codul pe care l-am scris pentru fereastra noastră:

 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); };

Dacă rulăm pluginul nostru acum, vom vedea că acum avem o fereastră deschisă care afișează interfața noastră cu utilizatorul web. Succes!

Din nou, înainte de a trece mai departe, să examinăm ce face codul pe care l-am adăugat:

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

Acest lucru ar trebui să pară familiar – este în esență același lucru cu ceea ce am făcut atunci când am creat NSWindow : alocați memorie pentru o vizualizare web, apoi inițializați-o.

 window.contentView = webView;

Această linie de cod spune ferestrei noastre să afișeze vizualizarea web pe care tocmai am făcut-o.

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

Aici scopul nostru este să creăm o adresă URL care să indice folderul web-ui care l-am creat mai devreme. Pentru a obține acea adresă URL, avem nevoie de o modalitate de a ne da seama unde se află pachetul pluginului nostru în sistemul de fișiere al utilizatorului. Aici folosim proprietatea context.scriptURL , care ne oferă adresa URL a scriptului care rulează în prezent . Cu toate acestea, acest lucru nu ne oferă un String JavaScript așa cum v-ați aștepta, ci o instanță a unei clase native, NSURL , care are câteva metode care facilitează manipularea șirurilor URL.

Trebuie să schimbăm ceea ce ne oferă context.scriptURL

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

— în:

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

Pas cu pas:

  1. Apelarea URLByDeletingLastPathComponent() prima dată ne oferă file://path-to-your-plugin/Contents/Sketch/
  2. Apelarea URLByDeletingLastPathComponent() ne oferă din nou file://path-to-your-plugin/Contents/
  3. Și, în sfârșit, adăugarea Resources/web-ui/ la sfârșit folosind URLByAppendingPathComponent ("Resources/web-ui/") ne oferă file://path-to-your-plugin/Contents/Resources/web-ui/

De asemenea, trebuie să creăm o a doua adresă URL care să indice direct fișierul index.html :

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

În cele din urmă, îi spunem vizualizării noastre web să încarce index.html și să îi acordăm acces la conținutul dosarului web-ui :

 webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

În regulă. Până acum, avem o fereastră care afișează interfața noastră cu utilizatorul web, așa cum ne-am dorit. Cu toate acestea, nu este încă complet - designul nostru original nu are o bară de titlu (sau „crom”), dar fereastra noastră actuală are. Există, de asemenea, faptul că, atunci când facem clic în interiorul unui document Sketch, acel document se mișcă în fața ferestrei noastre, ceea ce nu este ceea ce ne dorim - dorim ca utilizatorul să poată interacționa cu fereastra pluginului și documentul Sketch fără a fi necesar reorientați constant de la o fereastră la alta.

Pentru a remedia acest lucru, trebuie mai întâi să scăpăm de cromul implicit al ferestrei și să păstrăm doar butoanele. Adăugarea celor două linii de cod de mai jos va scăpa de bara de titlu.

Notă: Ca și înainte, toate proprietățile și metodele pe care le folosim mai jos sunt documentate în pagina de documentație a NSWindow .

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

Următoarele două linii de cod vor elimina butoanele ferestrei (cunoscute și sub numele de „semafoare” în limbajul MacOS) de care nu avem nevoie – „zoom” și „minimizați” – lăsând doar butonul „închidere”:

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

În timp ce suntem la asta, să continuăm și să schimbăm culoarea de fundal a ferestrei pentru a se potrivi cu cea a interfeței noastre web:

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

În continuare, trebuie să facem ceva pentru a menține fereastra plugin-ului plutitoare deasupra altor ferestre, astfel încât utilizatorul să poată interacționa cu documentele Sketch fără a fi nevoit să-și facă griji cu privire la dispariția ferestrei Mosaic. Putem folosi un tip special de NSWindow pentru aceasta, numit NSPanel , care este capabil să „rămână deasupra” altor ferestre. Tot ceea ce este necesar pentru aceasta este să schimbați NSWindow în NSPanel , care este o schimbare de cod pe o singură linie:

 const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Acum îi spunem ferestrei noastre panoului să plutească (rămîne deasupra tuturor celorlalte) și să concentrăm doar tastatura/mouse-ul atunci când este necesar:

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

De asemenea, putem modifica fereastra astfel încât să se redeschidă automat în ultima poziție în care se afla:

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

Această linie spune practic „amintește-ți poziția acestei ferestre salvând-o cu preferințele Sketch sub cheia mosaic-panel-frame ”.

Toate împreună, avem acum următorul cod:

 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); };

Organizarea Codului

Înainte de a trece la următoarea parte, este o idee bună să ne organizăm codul astfel încât să fie mai ușor de navigat și modificat. Deoarece avem încă mult mai mult cod de adăugat și dorim să evităm ca index.js să devină un teren dezordonat pentru tot codul nostru, să împărțim puțin lucrurile și să mutăm codul nostru specific UI într-un fișier numit ui.js , sub folderul Sketch . De asemenea, vom extrage unele dintre sarcinile UI pe care le facem, cum ar fi crearea vizualizării web și a ferestrei, în propriile funcții.

Creați un fișier nou numit ui.js și introduceți codul de mai jos în el:

 // 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 };

Există câteva modificări cheie pe care le-am făcut aici, care sunt importante de reținut. Pe lângă faptul că am creat funcții specifice pentru crearea, ascunderea și afișarea ferestrei noastre și a vizualizării sale web, am modularizat și codul interfeței cu utilizatorul.

Observați linia module.exports = { loadAndShow, cleanup } din partea de jos? Aceasta este o modalitate pentru noi de a specifica exact ce obiecte și funcții scripturile care importă acest cod UI le pot folosi (și le ascunde pe cele despre care nu vrem să-și facă griji), ceea ce înseamnă că acum avem un API mai organizat pentru a interacționa, arătând și distrugând interfața noastră de utilizare.

Lectură recomandată : Dezlănțuirea întregului potențial al simbolurilor în schiță

Să vedem cum arată asta în practică. Înapoi în index.js , eliminați vechiul cod și adăugați următoarele:

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

Folosim o funcție specială pe care Sketch o pune automat la dispoziție, require , pentru a importa codul nostru ui.js și a atribui modulul returnat variabilei UI . Acest lucru ne oferă acces la un API simplificat pentru declanșarea interfeței noastre cu utilizatorul. Lucrurile sunt mult mai ordonate acum și ușor de găsit!

Concluzie

Bravo - ai ajuns departe! 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!