Jak zbudować wtyczkę szkicu za pomocą JavaScript, HTML i CSS (część 1)

Opublikowany: 2022-03-10
Krótkie podsumowanie ↬ Jeśli kiedykolwiek pracowałeś ze Sketchem, istnieje duże prawdopodobieństwo, że było wiele momentów, kiedy pomyślałeś: „Gdyby tylko Sketch mógł zrobić tę jedną konkretną rzecz, byłbym w stanie wykonać to zadanie pod ręką znacznie szybciej, łatwiej i lepiej.” Cóż, nie martw się już! W tym dwuczęściowym artykule dowiesz się, jak tworzyć własne wtyczki Sketch od podstaw — dając ci umiejętności potrzebne do rozwiązywania dokładnie tego rodzaju problemów.

Ten samouczek jest przeznaczony dla osób, które znają i korzystają z aplikacji Sketch i nie boją się bawić kodem. Aby czerpać z tego największe korzyści, będziesz musiał mieć przynajmniej podstawowe doświadczenie w pisaniu JavaScript (i opcjonalnie HTML/CSS).

Wtyczka, którą będziemy tworzyć, nazywa się „Mozaika”. W części pierwszej poznamy podstawowe pliki, które składają się na wtyczkę Sketch; napiszemy trochę JavaScript i stworzymy interfejs użytkownika dla naszej wtyczki za pomocą niektórych HTML i CSS. Kolejny artykuł będzie o tym, jak podłączyć interfejs użytkownika do podstawowego kodu wtyczki, jak zaimplementować główne funkcje wtyczki, a na jego końcu dowiesz się również, jak zoptymalizować kod i sposób działania wtyczki.

Podzielę się również kodem wtyczki (JS, HTML, CSS) oraz plikami, które będziesz mógł przejrzeć i wykorzystać do celów edukacyjnych.

Czym są wtyczki szkicu i jak działają?

W programie Sketch wtyczki są sposobem na dodawanie funkcji i funkcjonalności, których nie ma w programie Sketch „po wyjęciu z pudełka”. Biorąc pod uwagę, że prawie zawsze będzie brakować jakiejś funkcji lub integracji w danym programie (szczególnie biorąc pod uwagę ogromną liczbę potrzeb każdego indywidualnego projektanta!), można zacząć wyobrażać sobie, jak wtyczki mogą być szczególnie przydatne i potężne. Wtyczki Sketch są w stanie zrobić prawie wszystko, czego można oczekiwać, na przykład manipulować kolorem, kształtem, rozmiarem, kolejnością, stylem, grupowaniem i efektami warstw, ale także mogą robić takie rzeczy, jak wysyłanie żądań do zasobów internetowych, prezentowanie użytkownika interfejs i wiele, wiele więcej!

Od strony programowania wszystkie wtyczki Sketch są napisane w kodzie JavaScript. Cóż, właściwie to nie do końca prawda. Bardziej trafne jest stwierdzenie, że większość wtyczek Sketch jest napisana w JavaScript, ponieważ możliwe jest również napisanie wtyczki Sketch w jednym z języków programowania Apple, Objective-C i Swift, chociaż nawet one wymagają niewielkiej wiedzy na temat JavaScript.

Nie martw się jednak. W tym artykule skupimy się na tworzeniu wtyczek Sketch przy użyciu samego JavaScriptu, HTML i CSS . Nie będziemy zagłębiać się w podstawy HTML, CSS czy JavaScript — ten artykuł zakłada przynajmniej pewną wiedzę i doświadczenie z wszystkimi tymi trzema. Witryna programisty MDN to doskonałe miejsce, w którym można dowiedzieć się więcej o tworzeniu stron internetowych.

Więcej po skoku! Kontynuuj czytanie poniżej ↓

Zacznijmy!

Po pierwsze, co robimy?

W tym samouczku nauczę Cię, jak zbudować podstawową, przyjazną dla początkujących wtyczkę, która będzie w stanie tworzyć, powielać i modyfikować warstwy, a także prezentować użytkownikowi ładny interfejs użytkownika. Robiąc to, moim celem jest zdobycie podstawowej wiedzy, na której można bazować i używać jej do tworzenia własnych wtyczek.

Wtyczka, którą będziemy budować, nazywa się Mosaic i jest w rzeczywistości „generatorem wzorców”. Nakarm go swoimi warstwami, dostosuj kilka ustawień, a utworzy wzór:

Obraz przedstawiający interfejs użytkownika wtyczki Mosaic i kilka przykładowych wzorców.
Interfejs użytkownika Mosaic i kilka przykładów wzorów wykonanych za jego pomocą. (duży podgląd)

Jeśli chcesz zainstalować i bawić się Mosaic, możesz pobrać ukończoną wtyczkę z GitHub.

Trochę historii: Mosaic jest w dużej mierze inspirowany staromodną wtyczką Adobe Fireworks o nazwie Twist-and-Fade . Twist-and-Fade był dość potężny, mógł powielać warstwę dowolną liczbę razy, dostosowując jej odcień, położenie, obrót, rozmiar i krycie. Wtyczka była nawet w stanie generować animowane pliki GIF, takie jak ten, w którym utworzyła ramki dla dwóch obracających się elementów na taśmie magnetofonowej:

Obraz przedstawiający kasetę magnetofonową z obracającymi się bębnami
Animowana kaseta magnetofonowa (źródło). (duży podgląd)

(Oto film prezentujący Twist and Fade, jeśli chcesz zobaczyć, jak to działa).

Na potrzeby tego samouczka zbudujemy nieco podobną wtyczkę do Sketch, choć celowo uproszczoną, aby samouczek był jak najbardziej dostępny. W szczególności nasza wtyczka będzie w stanie:

  • Powiel dowolną warstwę szkicu (mapę bitową lub wektor) i dostosuj położenie, obrót i krycie warstwy duplikatów. To da nam wprowadzenie do manipulowania warstwami za pomocą API JavaScript Sketch.
  • Wyświetl interfejs użytkownika utworzony przy użyciu HTML, CSS i JS, który nauczy Cię, jak łatwo utworzyć interfejs dla wtyczki, korzystając z technologii internetowych, które być może już znasz. Interfejs wtyczki jest bardzo ważny, ponieważ w ten sposób zbieramy dane wejściowe użytkownika dotyczące tego, jak użytkownik chce, aby wyglądał wynikowy obraz mozaiki.

Tworzenie naszej podstawowej wtyczki w dziesięć sekund na płasko

Najpierw stworzymy „podstawę” (lub szablon) dla wtyczki, którą chcemy zbudować. Moglibyśmy stworzyć wszystkie potrzebne pliki i foldery, które tworzą wtyczkę, ręcznie, ale na szczęście nie musimy — ponieważ Sketch może to zrobić za nas. Po wygenerowaniu wtyczki szablonu będziemy mogli dostosować ją według własnego uznania.

Istnieje naprawdę szybka i łatwa technika, której możemy użyć do stworzenia wtyczki szablonu, która jest moją podstawową metodą, gdy muszę połączyć wtyczkę, aby rozwiązać problem, z którym mam do czynienia w danym momencie. Oto jak to działa:

Otwórz Szkic, sprawdź pasek menu u góry ekranu i kliknij Plugins -> Run Script . Spowoduje to otwarcie okna dialogowego, którego możemy użyć do przetestowania i uruchomienia kodu. Możemy również zapisać dowolny kod, który w nim wprowadzimy, jako wtyczkę, która jest tą częścią, która szczególnie nas teraz interesuje.

Wyczyść kod, który jest już w tym oknie dialogowym i zastąp go następującym kodem demonstracyjnym:

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

Następnie naciśnij Save Script as Plugin w lewym dolnym rogu okna, wprowadź dowolną nazwę, jaką chcesz, aby ta wtyczka miała (w naszym przypadku jest to „Mozaika”), a następnie ponownie Save Script as Plugin .

Naciśnij „Zapisz” w lewym dolnym rogu okna i wprowadź dowolną nazwę, jaką chcesz, aby ta wtyczka miała. (duży podgląd)

Wierz lub nie, ale już skończyliśmy — pozostaje tylko zjeść ciasto, które właśnie upieczyliśmy. Nadchodzi zabawna część. Po ponownym otwarciu menu wtyczek, powinieneś zobaczyć coś takiego: twoja nowa wtyczka, wymieniona jako „Mozaika”! Kliknij na to!

(duży podgląd)

Gratulacje, właśnie napisałeś swoją pierwszą wtyczkę Sketch!

To, co powinieneś zobaczyć po kliknięciu „Mozaika”, powinno przypominać krótki film powyżej, z dyskretną podpowiedź pojawiającą się na dole ekranu, rozpoczynającą się od słów „Hej, tam…” — dokładnie tak mówi wklejony przez nas kod do zrobienia. To właśnie sprawia, że ​​ta technika jest tak wspaniała: ułatwia wklejanie, modyfikowanie i testowanie kodu bez konieczności budowania wtyczki od zera. Jeśli znasz lub kiedykolwiek grałeś z konsolą internetową swojej przeglądarki, to w zasadzie to. Posiadanie tego narzędzia w tylnej kieszeni podczas budowania i testowania kodu jest koniecznością.

Zróbmy krótkie podsumowanie tego, co robi dodany kod:

Najpierw importuje moduł sketch/ui wbudowanej biblioteki JS Sketch i przypisuje go do zmiennej UI . Ten moduł zawiera kilka przydatnych metod związanych z interfejsem, z których jedną użyjemy:

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

Następnie wywołuje metodę message (która jest częścią modułu sketch/ui ) z ciągiem tekstu, który chcemy wyświetlić w podpowiedzi, którą widzieliśmy:

 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() to świetny sposób na zaprezentowanie użytkownikowi dyskretnej wiadomości; jest świetny w przypadkach, w których nie musisz kraść fokusu (niemodalny) i nie potrzebujesz żadnych wymyślnych przycisków ani pól tekstowych. Istnieją również inne sposoby prezentowania typowych elementów interfejsu użytkownika, takich jak alerty, podpowiedzi itp., z których część będziemy używać podczas tworzenia Mozaiki.

Dostosowywanie metadanych naszej wtyczki

Mamy teraz podstawową wtyczkę, od której można zacząć, ale nadal musimy ją jeszcze bardziej ulepszyć i sprawić, by była naprawdę nasza. Naszym kolejnym krokiem będzie zmiana metadanych wtyczki.

W tym kroku musimy zajrzeć do tzw. pakietu wtyczek . Po naciśnięciu przycisku Zapisz w oknie „Uruchom skrypt”, Sketch zapisał wtyczkę jako folder o nazwie Mosaic.sketchplugin , który można znaleźć w katalogu ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins . To trochę długie i denerwujące do zapamiętania; jako skrót, możesz go również otworzyć za pomocą Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder . Mimo że pojawia się w Finderze jako pojedynczy plik, w rzeczywistości jest to folder zawierający wszystko, czego potrzebuje nasza wtyczka, aby Sketch mógł go uruchomić. Powodem, dla którego pojawia się jako pojedynczy plik, mimo że jest folderem, jest to, że podczas pierwszej instalacji Sketch, Sketch zarejestrował rozszerzenie .sketchplugin jako „pakiet” (specjalny rodzaj folderu, który pojawia się jako plik) i poprosił o automatyczne otwarcie w szkicu po otwarciu.

Zajrzyjmy do środka. Kliknij prawym przyciskiem myszy Mosaic.sketchplugin , a następnie kliknij „Pokaż zawartość pakietu”. Wewnątrz powinieneś zobaczyć następującą strukturę katalogów:

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

Być może zastanawiasz się, dlaczego jest tam plik z rozszerzeniem .cocoascript . Nie martw się — to tylko zwykły plik JavaScript i zawiera tylko kod, który wprowadziliśmy wcześniej. Śmiało i zmień nazwę tego pliku na index.js , co zmieni strukturę katalogów na taką, jak poniżej:

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

Najpopularniejszy sposób organizowania plików w pakiecie wtyczek jest następujący: Twój kod (JavaScript) i manifest.json należą do Sketch/ , a zasoby (obrazy, pliki audio, pliki tekstowe itp.) należą do Resources/ .

Zacznijmy od zmodyfikowania pliku o nazwie manifest.json . Otwórz go w swoim ulubionym edytorze kodu, takim jak Visual Studio Code lub Atom.

Zobaczysz, że w tej chwili jest tu stosunkowo niewiele, ale wkrótce dodamy więcej. Manifest wtyczki służy przede wszystkim dwóm celom:

  1. Po pierwsze, dostarcza metadane opisujące wtyczkę użytkownikowi — takie jak nazwa, wersja, nazwisko autora i tak dalej. Sketch wykorzystuje te informacje w oknie dialogowym Sketch -> Preferences -> Plugins , aby utworzyć listę i opis wtyczki.
  2. Po drugie, informuje również Sketch o tym, jak zabrać się do pracy; oznacza to, że mówi Sketchowi, jak ma wyglądać menu wtyczki, jakie skróty klawiszowe przypisać do wtyczki i gdzie znajduje się kod wtyczki (aby Sketch mógł go uruchomić).

Biorąc pod uwagę cel #1, opisanie wtyczki użytkownikowi, prawdopodobnie zauważysz, że w tej chwili nie podano opisu ani autora, co byłoby mylące dla użytkownika i utrudniałoby identyfikację wtyczki. Naprawmy to, dostosowując wartości odpowiednich kluczy do:

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

Następnie dostosujmy identyfikator wtyczki. Ten identyfikator wykorzystuje tak zwaną „odwróconą notację domeny”, która jest naprawdę zwięzłym (lub nudnym, wybierz swój wybór) sposobem powiedzenia „weź domenę swojej witryny, odwróć kolejność, a następnie umieść nazwę swojego produktu na końcu”. Będzie to wyglądać następująco: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct .

Nie musisz trzymać się tej konwencji nazewnictwa — możesz umieścić tutaj, co chcesz, o ile jest na tyle unikatowe, aby uniknąć konfliktów z innymi wtyczkami (choć prawdopodobnie dobrym pomysłem jest trzymanie się formatu RDN, zwłaszcza że zapewnia prosty, wielokrotnego użytku system dla identyfikatorów wtyczek).

W tym celu zmień swój identyfikator na com.your-name.mosaic :

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

Osobiście lubię brać wszystkie klucze związane z metadanymi (tytuł, autor, identyfikator itp.) i grupować je u góry manifestu, aby nie były rozrzucone po całym miejscu i pomagały zachować zdrowie psychiczne, gdy muszę je znaleźć .

Następnie spójrzmy na klawisze menu i commands . Ci dwaj są odpowiedzialni za poinformowanie Sketch, jaki kod wywołać i w odpowiedzi na co.

Jeśli spojrzysz na klawisz menu , zobaczysz, że zawiera on klawisz title , którego wartością jest nazwa, pod którą pojawi się nasza wtyczka w menu Plugins . Posiada również klucz items , który jest listą identyfikatorów poleceń :

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

Obecnie na tej liście jest tylko jeden identyfikator polecenia, "com.bohemiancoding.sketch.runscriptidentifier" . Identyfikatory poleceń zawsze wskazują polecenie na liście commands . W tej chwili nasza wtyczka ma tylko jedno polecenie, które ma ten identyfikator:

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

Za każdym razem, gdy dodasz identyfikator polecenia do wpisu menu , Sketch wyszuka wpis polecenia, który ma ten identyfikator i wyświetli wartość jego klucza name (który w tym przypadku to „Mozaika”) i zamiast tego pokaże go w menu wtyczki identyfikatora.

Jeśli chodzi o rolę, jaką odgrywają polecenia, możemy pomyśleć o wpisie polecenia jako o sposobie poinformowania Sketch, jaką funkcję w kodzie JavaScript naszej wtyczki chcemy uruchomić po wywołaniu tego polecenia, przy czym „wywołanie” zwykle polega na kliknięciu przez użytkownika powiązanego menu przedmiot. Wpis polecenia nie robi nic sam, to po prostu JSON — po prostu zapewnia szkicowi opis , gdzie szukać kodu JavaScript, który musi uruchomić, gdy polecenie jest wywoływane.

Do tej pory mówiliśmy o tym, co robią klucze name i identifier polecenia, ale istnieją dwa inne klucze w poleceniu, którymi należy się zająć: script i handlers .

Klucz script informuje Sketch, gdzie znajduje się plik JavaScript, który powinien uruchomić. Zwróć uwagę, że Sketch zakłada, że ​​plik skryptu, o którym mowa, znajduje się w folderze Sketch/ , dlatego dla uproszczenia upewnij się, że cały kod JavaScript znajduje się w folderze Sketch/ . Zanim przejdziemy od tego klucza , ważne jest, aby upewnić się, że zmieniłeś wartość tego klucza na index.js , tak jak wcześniej zmieniliśmy nazwę pliku. W przeciwnym razie Sketch nie będzie mógł znaleźć i uruchomić twojego pliku JavaScript.

Wartość klucza handlers jest tym, na co Sketch patrzy, aby określić, jaką funkcję w JavaScript należy wywołać. Tutaj mamy tylko jeden zestaw obsługi: run , z wartością onRun . run to nazwa wstępnie zdefiniowanej, wbudowanej akcji szkicowania . Ta akcja run będzie zawsze wywoływana, gdy użytkownik kliknie element menu, który odwołuje się do tego polecenia. onRun to nazwa funkcji w automatycznie wygenerowanym pliku script.cocoascript (który zmieniliśmy na index.js ) oraz funkcji, którą chcemy wywołać, gdy wystąpi zdarzenie run , tj. gdy użytkownik kliknie element menu.

W naszym dotychczasowym przykładzie ten proces przebiega mniej więcej tak:

  1. Użytkownik klika nasz element menu.
  2. Szkic znajdzie polecenie skojarzone z tym elementem menu.
  3. Sketch znajduje plik skryptu, do którego odnosi się polecenie, i uruchamia go (co w tym przypadku oznacza, że ​​wykonuje JavaScript w index.js ).
  4. Ponieważ to polecenie zostało wywołane przez kliknięcie elementu menu, jest uważane za akcję run . Oznacza to, że Sketch przyjrzy się wartości handlers.run polecenia w celu wywołania następnej funkcji, która w tym przypadku to onRun .
  5. Sketch wywołuje funkcję onRun .

Polecenia są najczęściej wywoływane w odpowiedzi na kliknięcie przez użytkownika jednego z elementów menu, ale można je również wywoływać w odpowiedzi na inne działania użytkownika, takie jak zmiana przez użytkownika zaznaczenia lub właściwości na warstwie. Jednak w przypadku tej wtyczki nie będziemy używać żadnych innych działań. (Więcej informacji o akcjach i sposobie ich działania znajdziesz na stronie pomocy Action API).

Zanim przejdziemy od tego manifestu, będziemy chcieli wprowadzić dwie inne poprawki. W tej chwili nasze menu ma strukturę:

 Mosaic └ Mosaic 
Obraz przedstawiający element menu Mosaic nadmiarowo zagnieżdżony w innym menu o nazwie Mosaic
Dość zbędne, prawda? (duży podgląd)

…co jest trochę zbędne, ponieważ nasza wtyczka ma tylko jedną pozycję w menu. Dodaje to również trochę niepotrzebnego tarcia dla naszego użytkownika, ponieważ nasza wtyczka wymaga teraz dwóch kliknięć zamiast jednego. Możemy to naprawić, dodając isRoot: true do naszego menu :

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

Dzięki temu Sketch ma umieścić pierwszy poziom pozycji menu bezpośrednio pod menu Plugins , zamiast zagnieżdżać je pod title menu .

Naciśnij Zapisz i wróć do Sketch. Powinieneś zobaczyć, że teraz Mosaic -> Mosaic zostało zastąpione właśnie Mosaic — idealnie!

Obraz przedstawiający interfejs użytkownika wtyczki Mosaic
Interfejs użytkownika mozaiki. (duży podgląd)

Jeśli chodzi o naszą drugą poprawkę, przejdźmy dalej i zmieńmy nazwę tego identyfikatora polecenia na coś mniej nieporęcznego. Ponieważ identyfikatory poleceń muszą być unikalne tylko w kontekście pojedynczej wtyczki, możemy bezpiecznie zmienić ich nazwę na coś bardziej zwięzłego i oczywistego, na przykład "open" :

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

Zanim przejdziemy dalej, warto zauważyć, że menu mogą zawierać również inne menu. Możesz łatwo utworzyć podmenu, zagnieżdżając inny wpis { title: ..., items: ... } wewnątrz listy items innego menu:

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

Budowanie interfejsu użytkownika wtyczki

Do tej pory napisaliśmy kod demonstracyjny i dostosowaliśmy manifest naszej wtyczki. Przejdziemy teraz do tworzenia interfejsu użytkownika, który jest zasadniczo stroną internetową osadzoną w oknie (podobnie jak w przeglądarkach, których używasz):

Okno wtyczki. (duży podgląd)
Obraz przedstawiający komponenty tworzące interfejs naszej wtyczki: okno i widok sieciowy
Komponenty tworzące naszą wtyczkę. (duży podgląd)

Okno

Projekt interfejsu użytkownika Mosaic ma swoje własne okno, które możemy uznać za najbardziej podstawowy składnik; zaczniemy od tego. Aby utworzyć i wyświetlić okno, będziemy musieli skorzystać z klasy wbudowanej domyślnie w macOS, o nazwie NSWindow . W pozostałej części tego samouczka będziemy to robić całkiem sporo (używając wbudowanych interfejsów API, takich jak NSWindow ), co może wydawać się nieco zniechęcające, jeśli nie jesteś z nim zaznajomiony, ale nie martw się — wyjaśnię wszystko po drodze!

Uwaga: chociaż mówimy o wbudowanych interfejsach API, powodem, dla którego możemy w ogóle używać tej klasy, jest most obecny w środowisku wykonawczym JavaScript używanym przez wtyczki Sketch. Ten most automatycznie importuje te wbudowane klasy, metody i funkcje, które normalnie byłyby dostępne tylko dla aplikacji natywnych.

Otwórz Sketch/index.js w edytorze kodu, usuń to, co już tam jest, i wklej następujące:

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

Przyjrzyjmy się, co robi ten pierwszy fragment kodu:

 function onRun(context){

Pamiętasz wcześniej, kiedy rozmawialiśmy o poleceniach i ich działaniu, i powiedzieliśmy Sketchowi, aby wywołał w odpowiedzi na kliknięcie w menu wywołane onRun ? (Jeśli potrzebujesz odświeżenia, wróć do tej części powyżej, a następnie wróć.) Wszystko, co robi, to utworzenie tej funkcji. Zauważysz również, że nasza funkcja onRun pobiera argument context . Jest to argument, który Sketch będzie używał do obsługi poleceń, który może dostarczyć nam pewnych informacji. Później użyjemy go, aby uzyskać adres URL naszego pakietu wtyczek na komputerze użytkownika.

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

Tutaj właściwie robimy kilka rzeczy:

  1. Najpierw wywołujemy alloc() na NSWindow ; to w zasadzie oznacza „odłożyć trochę pamięci na instancję NSWindow”. Wystarczy wiedzieć, że będziesz musiał to zrobić dla każdej instancji natywnej klasy, którą chcesz utworzyć. Metoda alloc jest dostępna w każdej klasie natywnej.
  2. Następnie wywołujemy metodę inicjującą NSWindow (to znaczy metodę, która faktycznie tworzy instancję NSWindow ), o nazwie initWithContentRect:styleMask:backing:defer: . Zauważysz, że różni się to od tego, co nazywamy w powyższym kodzie — między każdym argumentem jest kilka dwukropków ( : ). Ponieważ nie możemy użyć tej składni w JavaScript, Sketch zmienia jej nazwę na taką, której faktycznie możemy użyć, zastępując dwukropki podkreśleniami, w ten sposób otrzymujemy jego nazwę JS: initWithContentRect_styleMask_backing_defer .
  3. Następnie przekazujemy każdy z argumentów, których potrzebuje metoda. Jako pierwszy argument, contentRect , dostarczamy prostokąt o rozmiarze wystarczająco dużym dla naszego interfejsu użytkownika.
  4. W przypadku styleMask używamy maski bitowej, która mówi, że chcemy, aby nasze okno miało przycisk zamykania, pasek tytułu i miało możliwość zmiany rozmiaru.
  5. Następne dwa argumenty, backing i defer , zawsze będą ustawione na NSBackingStoreBuffered i false , więc tak naprawdę nie musimy się nimi martwić. (Dokumentacja tej metody zawiera bardziej szczegółowe informacje, dlaczego tak się dzieje.)
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

Tutaj ustawiamy NSWindow releasedWhenClosed false , co oznacza: „Hej! nie usuwaj tego okna z pamięci tylko dlatego, że użytkownik je zamyka.” Następnie wywołujemy makeKeyAndOrderFront (null) , co oznacza: „Przenieś to okno na pierwszy plan i nadaj mu fokus klawiatury”.

Widok sieciowy: interfejs

Aby było łatwiej, napisałem już kod HTML i CSS interfejsu użytkownika wtyczki, którego będziemy używać; jedyny pozostały kod, który będziemy musieli do niego dodać, będzie dotyczył upewnienia się, że jesteśmy w stanie komunikować się między nim a naszym kodem wtyczki Sketch.

Następnie pobierz kod HTML i CSS. Po pobraniu wypakuj go, a następnie przenieś folder o nazwie „web-ui” do folderu Resources naszej wtyczki.

Uwaga : Pisanie i optymalizowanie rzeczywistego kodu HTML/CSS wykracza poza zakres tego samouczka, ponieważ koncentruje się on na języku JavaScript, który obsługuje podstawowe funkcje wtyczki; ale w sieci jest mnóstwo samouczków na ten temat, jeśli chcesz dowiedzieć się więcej.

Jeśli uruchomisz teraz naszą wtyczkę, zobaczysz, że pokazuje okno — tak, postęp! Ale jest pusty, bez tytułu i jeszcze niezbyt przydatny. Musimy go zdobyć, aby pokazać nasz interfejs sieciowy. W tym celu musimy użyć innej natywnej klasy, WKWebView , która jest widokiem specjalnie stworzonym do wyświetlania treści internetowych.

Dodamy kod potrzebny do stworzenia naszego WKWebView pod kodem, który napisaliśmy dla naszego okna:

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

Jeśli teraz uruchomimy naszą wtyczkę, zobaczymy, że teraz mamy otwarte okno, w którym wyświetla się nasz interfejs użytkownika sieciowego. Sukces!

Ponownie, zanim przejdziemy dalej, przyjrzyjmy się, co robi dodany przez nas kod:

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

Powinno to wyglądać znajomo — jest to w zasadzie to samo, co zrobiliśmy podczas tworzenia naszego NSWindow : przydziel pamięć dla widoku internetowego, a następnie go zainicjuj.

 window.contentView = webView;

Ten wiersz kodu mówi naszemu oknie, aby wyświetlić widok sieciowy, który właśnie stworzyliśmy.

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

Tutaj naszym celem jest stworzenie adresu URL, który wskazuje na folder web-ui , który stworzyliśmy wcześniej. Aby uzyskać ten adres URL, potrzebujemy sposobu, aby dowiedzieć się, gdzie znajduje się pakiet naszej wtyczki w systemie plików użytkownika. Tutaj używamy właściwości context.scriptURL , która daje nam adres URL aktualnie uruchomionego skryptu . Jednak nie daje nam to ciągu JavaScript, jak można by się spodziewać, ale instancję natywnej klasy String , NSURL ma kilka metod ułatwiających manipulowanie ciągami adresów URL.

Musimy zmienić to, co daje nam context.scriptURL

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

- do:

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

Krok po kroku:

  1. Wywołanie URLByDeletingLastPathComponent() po raz pierwszy daje nam file://path-to-your-plugin/Contents/Sketch/
  2. Ponowne wywołanie URLByDeletingLastPathComponent() daje nam file://path-to-your-plugin/Contents/
  3. I na koniec, dodanie Resources/web-ui/ na końcu za pomocą URLByAppendingPathComponent ("Resources/web-ui/") daje nam file://path-to-your-plugin/Contents/Resources/web-ui/

Musimy również utworzyć drugi adres URL, który wskazuje bezpośrednio na plik index.html :

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

Na koniec mówimy naszemu widokowi internetowemu, aby załadował index.html i dał mu dostęp do zawartości folderu web-ui :

 webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

W porządku. Do tej pory mamy okno, które wyświetla nasz webowy interfejs użytkownika, tak jak chcieliśmy. Jednak nie jest jeszcze ukończony — nasz oryginalny projekt nie ma paska tytułu (lub „chromu”), ale nasze obecne okno ma. Istnieje również fakt, że kiedy klikamy wewnątrz dokumentu szkicu, dokument ten przesuwa się przed naszym oknem, czego nie chcemy — chcemy, aby użytkownik mógł wchodzić w interakcję z oknem wtyczki i dokumentem szkicu bez konieczności stale przechodź z jednego okna do drugiego.

Aby to naprawić, musimy najpierw pozbyć się domyślnego chromowania okna i zachować tylko przyciski. Dodanie dwóch linijek kodu poniżej pozbędzie się paska tytułowego.

Uwaga: Tak jak poprzednio, wszystkie właściwości i metody, których używamy poniżej, są udokumentowane na stronie dokumentacji NSWindow .

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

Te dwa następne wiersze kodu usuną przyciski okna (znane również jako „sygnalizacja świetlna” w żargonie MacOS), których nie potrzebujemy — „zoom” i „minimalizuj” — pozostawiając tylko przycisk „zamknij”:

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

Skoro już przy tym jesteśmy, przejdźmy dalej i zmieńmy kolor tła okna, aby dopasować go do naszego internetowego interfejsu użytkownika:

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

Następnie musimy coś zrobić, aby nasze pływające okno wtyczki znajdowało się nad innymi oknami, aby użytkownik mógł wchodzić w interakcje z dokumentami Sketch bez martwienia się, że okno Mozaiki zniknie. Możemy do tego użyć specjalnego typu NSWindow , zwanego NSPanel , który jest w stanie „pozostać na wierzchu” innych okien. Wszystko, co jest potrzebne do tego, to zmiana NSWindow na NSPanel , co jest jednowierszową zmianą kodu:

 const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Teraz mówimy naszemu panelowi, aby unosiło się (pozostało nad wszystkimi innymi) i wybierało fokus klawiatury/myszy tylko wtedy, gdy jest to konieczne:

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

Możemy również dostosować nasze okno, aby automatycznie otwierało się w ostatniej pozycji, w której było:

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

Ta linia zasadniczo mówi „zapamiętaj położenie tego okna, zapisując je z preferencjami Sketch pod kluczową mosaic-panel-frame ”.

W sumie mamy teraz następujący kod:

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

Porządkowanie Kodeksu

Zanim przejdziemy do następnej części, dobrym pomysłem jest uporządkowanie naszego kodu tak, aby był łatwiejszy w nawigacji i dostrajaniu. Ponieważ wciąż mamy dużo więcej kodu do dodania i chcemy uniknąć sytuacji, w której index.js stanie się bałaganem dla całego naszego kodu, podzielmy trochę rzeczy i przenieśmy nasz kod specyficzny dla interfejsu użytkownika do pliku o nazwie ui.js , w folderze Sketch . Wyodrębnimy również niektóre z wykonywanych przez nas zadań interfejsu użytkownika, takich jak tworzenie widoku internetowego i okna, do ich własnych funkcji.

Utwórz nowy plik o nazwie ui.js i wstaw do niego poniższy kod:

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

Wprowadziliśmy tutaj kilka kluczowych zmian, na które warto zwrócić uwagę. Oprócz tego, że stworzyliśmy specyficzne funkcje do tworzenia, ukrywania i pokazywania naszego okna i jego widoku internetowego, zmodularyzowaliśmy również nasz kod interfejsu użytkownika.

Zwróć uwagę na module.exports = { loadAndShow, cleanup } na dole? W ten sposób możemy dokładnie określić, jakich obiektów i funkcji mogą używać skrypty importujące ten kod interfejsu użytkownika (i ukrywać te, którymi nie chcemy się martwić), co oznacza, że ​​mamy teraz bardziej zorganizowane API do interakcji, pokazywanie i niszczenie naszego interfejsu użytkownika.

Zalecana literatura : Uwolnienie pełnego potencjału symboli w szkicu

Zobaczmy, jak to wygląda w praktyce. Wróć do index.js , usuń stary kod i dodaj:

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

Używamy specjalnej funkcji, którą Sketch automatycznie udostępnia nam, require , aby zaimportować nasz kod ui.js i przypisać zwrócony moduł do zmiennej UI . Daje nam to dostęp do uproszczonego interfejsu API do uruchamiania naszego interfejsu użytkownika. Rzeczy są teraz o wiele bardziej uporządkowane i łatwe do znalezienia!

Wniosek

Dobra robota — zaszedłeś daleko! 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!