Как создать плагин Sketch с помощью JavaScript, HTML и CSS (часть 1)

Опубликовано: 2022-03-10
Краткое резюме ↬ Если вы когда-либо работали со Sketch, скорее всего, было много моментов, когда вы думали: «Если бы Sketch мог делать только одну конкретную вещь, я бы смог выполнить поставленную задачу». гораздо быстрее, проще и лучше». Ну, больше не грусти! В этой статье, состоящей из двух частей, вы узнаете, как создавать собственные плагины для Sketch с нуля, что даст вам навыки, необходимые для решения именно таких проблем.

Это руководство предназначено для людей, которые знают и используют приложение Sketch и не боятся возиться с кодом. Чтобы извлечь из этого максимальную пользу, вам потребуется хотя бы базовый опыт написания JavaScript (и, по желанию, HTML/CSS).

Плагин, который мы будем создавать, называется «Mosaic». В первой части мы узнаем об основных файлах, из которых состоит плагин Sketch; мы напишем немного JavaScript и создадим пользовательский интерфейс для нашего плагина с помощью HTML и CSS. Следующая статья будет о том, как подключить пользовательский интерфейс к основному коду плагина, как реализовать основные функции плагина, а в конце вы также узнаете, как оптимизировать код и как работает плагин.

Я также поделюсь кодом плагина (JS, HTML, CSS) и файлами, которые вы сможете изучить и использовать в учебных целях.

Что такое плагины для Sketch и как они работают?

В Sketch плагины — это способ добавления функций и функций, которых нет в Sketch «из коробки». Учитывая, что почти всегда в любой программе будет отсутствовать какая-то функция или интеграция (особенно учитывая огромное количество потребностей, которые могут возникнуть у любого отдельного дизайнера!), можно начать представлять, насколько плагины могут быть особенно полезными и мощными. Плагины Sketch могут делать почти все, что вы ожидаете, например, управлять цветом, формой, размером, порядком, стилем, группировкой и эффектами слоев, а также могут делать такие вещи, как делать запросы к интернет-ресурсам, представлять пользователю интерфейс и многое, многое другое!

Что касается программирования, все плагины Sketch написаны в коде JavaScript. Ну, на самом деле, это не совсем так. Точнее будет сказать, что большинство плагинов Sketch написаны на JavaScript, так как также можно написать плагин Sketch на одном из языков программирования Apple, Objective-C и Swift, хотя даже они требуют небольшого знания JavaScript.

Не волнуйтесь, хотя. В этой статье мы сосредоточимся на том, как создавать плагины для Sketch, используя только JavaScript, HTML и CSS. Мы не будем вдаваться в основы HTML, CSS или JavaScript — эта статья предполагает, по крайней мере, некоторые знания и опыт работы со всеми этими тремя. Веб-сайт для разработчиков MDN — отличное место, где можно узнать больше о веб-разработке.

Еще после прыжка! Продолжить чтение ниже ↓

Давайте начнем!

Во-первых, что мы делаем?

В этом уроке я научу вас, как создать базовый, удобный для начинающих плагин, который сможет создавать, дублировать и изменять слои, а также предоставлять пользователю приятный пользовательский интерфейс. Таким образом, моя цель — получить фундаментальные знания, на основе которых вы сможете построить и использовать их для создания собственных плагинов.

Плагин, который мы будем создавать, называется Mosaic и фактически является «генератором шаблонов». Скормите ему свои слои, настройте несколько параметров, и он создаст шаблон:

Изображение, показывающее пользовательский интерфейс плагина Mosaic и несколько примеров шаблонов.
Пользовательский интерфейс Mosaic и несколько примеров шаблонов, созданных с его помощью. (Большой превью)

Если вы хотите установить Mosaic и поиграть с ним, вы можете скачать готовый плагин с GitHub.

Немного истории: Mosaic во многом вдохновлен старым плагином Adobe Fireworks под названием Twist-and-Fade . Twist-and-Fade был довольно мощным, он мог дублировать слой любое количество раз, регулируя его оттенок, положение, поворот, размер и непрозрачность. Плагин даже мог создавать анимированные GIF-файлы, такие как этот, где он создавал кадры для двух вращающихся элементов на кассете:

Изображение, показывающее музыкальную кассету с вращающимися барабанами.
Анимационная кассета (источник). (Большой превью)

(Вот видео, демонстрирующее Twist и Fade, если вам интересно увидеть, как именно это работает.)

Для целей этого руководства мы создадим несколько похожий плагин для Sketch, хотя и намеренно упрощенный, чтобы сделать учебник максимально доступным. В частности, наш плагин сможет:

  • Дублируйте любой слой Sketch (растровый или векторный) и настройте положение, поворот и непрозрачность дубликатов слоя. Это познакомит нас с управлением слоями с помощью JavaScript API Sketch.
  • Отобразите пользовательский интерфейс, созданный с использованием HTML, CSS и JS, который научит вас, как легко создать интерфейс для подключаемого модуля с помощью веб-технологий, с которыми вы, возможно, уже знакомы. Интерфейс плагина очень важен, так как с его помощью мы собираем данные пользователя о том, как пользователь хочет, чтобы результирующее мозаичное изображение выглядело.

Создание нашего базового плагина за десять секунд

Во-первых, мы создадим «базу» (или шаблон) для подключаемого модуля, который мы хотим создать. Мы могли бы создать все необходимые файлы и папки, составляющие плагин, вручную, но, к счастью, нам это не нужно — Sketch может сделать это за нас. После того, как мы сгенерируем плагин шаблона, мы сможем настроить его по своему усмотрению.

Существует действительно быстрый и простой метод, который мы можем использовать для создания плагина шаблона, который в значительной степени является моим методом, когда мне нужно собрать плагин, чтобы решить любую проблему, с которой я сталкиваюсь в данный момент. Вот как это работает:

Открыв Sketch, проверьте строку меню в верхней части экрана и нажмите « Plugins -> Run Script . Это откроет диалоговое окно, которое мы можем использовать для тестирования и запуска кода. Мы также можем сохранить любой код, который мы вводим в него, как плагин, который нас сейчас особенно интересует.

Очистите весь код, который уже есть в этом диалоговом окне, и замените его следующим демонстрационным кодом:

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

Затем нажмите « Save Script as Plugin » в левом нижнем углу окна, введите любое имя, которое вы хотите, чтобы этот плагин имел (в нашем случае это «Мозаика»), затем еще раз « Save Script as Plugin ».

Нажмите «Сохранить» в левом нижнем углу окна и введите любое имя, которое вы хотите, чтобы этот плагин имел. (Большой превью)

Хотите верьте, хотите нет, но мы уже закончили — осталось только съесть пирог, который мы только что испекли. А вот и самое интересное. Открыв меню «Плагины» еще раз, вы должны увидеть что-то вроде этого: ваш совершенно новый плагин указан как «Мозаика»! Нажмите здесь!

(Большой превью)

Поздравляем, вы только что написали свой первый плагин для Sketch!

То, что вы должны увидеть после нажатия «Мозаика», должно быть похоже на короткое видео выше, с ненавязчивой всплывающей подсказкой, появляющейся в нижней части экрана, начинающейся со слов «Привет…» — именно это говорит код, который мы вставили. сделать. Вот что делает этот метод таким замечательным: он позволяет легко вставлять, изменять и тестировать код без необходимости создавать плагин с нуля. Если вы знакомы с веб-консолью вашего браузера или когда-либо играли с ней, это в основном так. Необходимо иметь этот инструмент в заднем кармане, когда вы создаете и тестируете код.

Давайте кратко рассмотрим, что делает добавленный вами код:

Во-первых, он импортирует модуль sketch/ui из встроенной JS-библиотеки Sketch и присваивает его переменной UI . Этот модуль содержит несколько полезных методов, связанных с интерфейсом, один из которых мы будем использовать:

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

Затем он вызывает метод message (который является частью модуля 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!");

Метод message() предоставляет отличный способ представить пользователю ненавязчивое сообщение; это отлично подходит для случаев, когда вам не нужно красть фокус (не модально) и не нужны какие-то причудливые кнопки или текстовые поля. Существуют также другие способы представления общих элементов пользовательского интерфейса, таких как оповещения, подсказки и т. д., некоторые из которых мы будем использовать при создании Mosaic.

Настройка метаданных нашего плагина

Теперь у нас есть базовый плагин, с которого можно начать, но нам еще нужно его доработать и сделать по-настоящему нашим. Нашим следующим шагом будет изменение метаданных плагина.

Для этого шага нам нужно заглянуть в то, что называется комплектом плагинов . Когда вы нажимаете «Сохранить» в окне «Выполнить скрипт», Sketch сохраняет ваш плагин в виде папки с именем Mosaic.sketchplugin , которую вы можете найти в каталоге ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins . Это немного длинно и неприятно запоминать; как ярлык, вы также можете открыть его через Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder . Несмотря на то, что он отображается в Finder как один файл, на самом деле это папка, содержащая все, что нужно нашему плагину для его запуска в Sketch. Причина, по которой он отображается как один файл, несмотря на то, что это папка, заключается в том, что когда вы впервые установили Sketch, Sketch зарегистрировал расширение .sketchplugin как «пакет» (папка особого типа, которая отображается как файл) и попросил его автоматически открыть. в Sketch при открытии.

Давайте заглянем внутрь. Щелкните правой кнопкой мыши Mosaic.sketchplugin и выберите «Показать содержимое пакета». Внутри вы должны увидеть следующую структуру каталогов:

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

Вам может быть интересно, почему там есть файл с расширением .cocoascript . Не беспокойтесь — это обычный файл JavaScript, содержащий только тот код, который мы ввели ранее. Переименуйте этот файл в index.js , что изменит структуру каталогов, чтобы она выглядела так, как показано ниже:

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

Наиболее распространенный способ организации файлов внутри пакета плагинов следующий: ваш код (JavaScript) и manifest.json находятся в Sketch/ , а ресурсы (например, изображения, аудиофайлы, текстовые файлы и т. д.) — в Resources/ .

Начнем с настройки файла с именем manifest.json . Откройте его в своем любимом редакторе кода, таком как Visual Studio Code или Atom.

Вы увидите, что на данный момент внутри здесь относительно мало вещей, но мы скоро добавим больше. Манифест плагина служит в основном двум целям:

  1. Во-первых, он предоставляет пользователю метаданные, описывающие плагин — такие вещи, как его имя, версия, имя автора и так далее. Sketch использует эту информацию в диалоговом окне Sketch -> Preferences -> Plugins , чтобы создать список и описание вашего плагина.
  2. Во-вторых, он также сообщает Sketch о том, как приступить к делу; то есть он сообщает Sketch, как должно выглядеть меню вашего плагина, какие горячие клавиши назначить вашему плагину и где находится код вашего плагина (чтобы Sketch мог его запустить).

Учитывая цель № 1, описание плагина для пользователя, вы, вероятно, заметите, что сейчас нет ни описания, ни автора, что могло бы сбить пользователя с толку и затруднить идентификацию плагина. Давайте исправим это, изменив значения соответствующих ключей на:

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

Далее настроим идентификатор плагина. Этот идентификатор использует так называемую «обратную нотацию домена», которая является очень кратким (или скучным, на ваш выбор) способом сказать «возьмите домен вашего сайта, измените порядок, а затем поставьте название вашего продукта в конце». Это будет выглядеть примерно так: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct .

Вам не обязательно придерживаться этого соглашения об именах — вы можете поместить сюда все, что хотите, если оно достаточно уникально, чтобы избежать конфликтов с другими плагинами (хотя, вероятно , лучше придерживаться формата RDN, тем более, что он обеспечивает простая система многократного использования для ваших идентификаторов плагинов).

Для этого измените свой идентификатор на com.your-name.mosaic :

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

Лично мне нравится брать все ключи, связанные с метаданными (название, автор, идентификатор и т. д.), и группировать их в верхней части манифеста, чтобы они не были разбросаны повсюду и помогали сохранить рассудок, когда мне нужно их найти. .

Далее, давайте взглянем на menu и клавиши commands . Эти двое отвечают за указание Sketch, какой код вызывать и в ответ на что.

Если вы посмотрите на ключ menu , вы увидите, что он содержит ключ title , значением которого является имя нашего плагина, которое будет отображаться в меню Plugins . У него также есть ключ items , который представляет собой список идентификаторов команд :

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

На данный момент в этом списке есть только один идентификатор команды: "com.bohemiancoding.sketch.runscriptidentifier" . Идентификаторы команд всегда указывают на команду в списке commands . Сейчас в нашем плагине есть только одна команда с таким идентификатором:

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

Всякий раз, когда вы добавляете идентификатор команды в пункт menu , Sketch будет искать запись команды, которая имеет этот идентификатор, и будет отображать значение ключа его name (в данном случае «Mosaic») и вместо этого показывать его в меню вашего плагина. идентификатора.

Что касается ролевой игры команд, мы можем думать о записи команды как о способе сообщить Sketch, какую функцию в коде JavaScript нашего плагина мы хотим запустить при вызове этой команды, причем «вызов» обычно представляет собой щелчок пользователя по соответствующему меню. пункт. Запись команды сама по себе ничего не делает, это просто JSON — она просто предоставляет Sketch описание того, где искать JavaScript, который необходимо запустить при вызове команды.

До сих пор мы говорили о том, что делают ключи name и identifier команды, но есть два других ключа в команде, которые необходимо решить: script и handlers .

Ключ script сообщает Sketch, где находится файл JavaScript, который он должен запустить. Обратите внимание, что Sketch предполагает, что рассматриваемый файл сценария находится в папке Sketch/ , поэтому для простоты вам нужно убедиться, что весь ваш код JavaScript находится где-то в папке Sketch/ . Прежде чем мы перейдем от этого ключа , важно убедиться, что вы изменили значение этого ключа на index.js , точно так же, как мы переименовали файл ранее. В противном случае Sketch не сможет найти и запустить ваш файл JavaScript.

Значение ключа handlers — это то, на что обращается Sketch, чтобы определить, какую функцию в вашем JavaScript вызывать. Здесь у нас есть только один набор обработчиков: run со значением onRun . run — это имя предопределенного встроенного действия Sketch. Это действие run всегда будет вызываться, когда пользователь щелкает пункт меню, который ссылается на эту команду. onRun — это имя функции в автоматически сгенерированном файле script.cocoascript (который мы переименовали в index.js ), и функция, которую мы хотим вызывать, когда происходит событие run , т. е. когда пользователь щелкает пункт меню.

В примере, который у нас есть до сих пор, этот процесс происходит примерно так:

  1. Пользователь нажимает на наш пункт меню.
  2. Sketch находит команду, связанную с этим пунктом меню.
  3. Sketch находит файл скрипта, на который ссылается команда, и запускает его (что в данном случае означает выполнение JavaScript в index.js ).
  4. Поскольку эта команда была вызвана щелчком пункта меню, она считается действием run . Это означает, что Sketch будет смотреть на значение handlers.run команды для следующей функции, которая будет вызываться в данном случае onRun .
  5. Sketch вызывает функцию onRun .

Команды чаще всего вызываются в ответ на щелчок пользователя по одному из пунктов вашего меню, но они также могут вызываться в ответ на другие действия пользователя, например изменение пользователем выбора или свойства слоя. Однако для этого плагина мы не будем использовать какие-либо другие действия. (Вы можете узнать больше о действиях и о том, как они работают, на странице справки API действий.)

Прежде чем мы перейдем от этого манифеста, мы хотим сделать две другие настройки. На данный момент наше меню имеет структуру:

 Mosaic └ Mosaic 
Изображение, показывающее элемент меню Mosaic, избыточно вложенный в другое меню с именем Mosaic
Довольно избыточно, не так ли? (Большой превью)

…что немного лишнее, так как наш плагин имеет только один пункт меню. Это также добавляет немного ненужного трения для нашего пользователя, так как наш плагин теперь вызывается двумя щелчками мыши, а не одним. Мы можем исправить это, добавив isRoot: true в наше menu :

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

Это говорит Sketch размещать элементы меню первого уровня непосредственно под меню « Plugins », а не вкладывать их под title меню.

Нажмите «Сохранить» и вернитесь в Sketch. Вы должны увидеть, что теперь Mosaic -> Mosaic заменена просто Mosaic — отлично!

Изображение, показывающее пользовательский интерфейс плагина Mosaic
Пользовательский интерфейс мозаики. (Большой превью)

Что касается нашей второй настройки, давайте продолжим и переименуем этот идентификатор команды во что-то менее громоздкое. Поскольку идентификаторы команд должны быть уникальными только в контексте отдельного плагина, мы можем безопасно переименовать его во что-то более лаконичное и очевидное, например "open" :

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

Прежде чем двигаться дальше, полезно отметить, что меню может содержать и другие меню. Вы можете легко создать подменю, вложив другую запись { title: ..., items: ... } в список items другого меню:

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

Создание пользовательского интерфейса плагина

На данный момент мы написали некоторый демонстрационный код и настроили манифест нашего плагина. Теперь мы перейдем к созданию его пользовательского интерфейса, который по сути представляет собой веб-страницу, встроенную в окно (аналогично браузерам, с которыми вы знакомы):

Окно плагина. (Большой превью)
Изображение, показывающее компоненты, из которых состоит интерфейс нашего плагина: окно и веб-представление.
Компоненты, из которых состоит наш плагин. (Большой превью)

Окно

Дизайн пользовательского интерфейса Mosaic имеет собственное окно, которое мы можем считать самым основным компонентом; мы начнем с него. Чтобы создать и отобразить окно, нам придется использовать класс, встроенный в macOS по умолчанию, который называется NSWindow . В оставшейся части этого руководства мы на самом деле будем делать это совсем немного (используя встроенные API, такие как NSWindow ), что может показаться немного сложным, если вы не знакомы с этим, но не волнуйтесь — я объясню все по пути!

Примечание. Хотя мы говорим о встроенных API, причина, по которой мы вообще можем использовать этот класс, заключается в мосте, присутствующем в среде выполнения JavaScript, используемой плагинами Sketch. Этот мост автоматически импортирует эти встроенные классы, методы и функции, которые обычно доступны только для собственных приложений.

Откройте Sketch/index.js в редакторе кода, удалите то, что уже есть, и вставьте следующее:

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

Давайте посмотрим, что делает этот первый фрагмент кода:

 function onRun(context){

Помните ранее, когда мы говорили о командах и о том, как они работают, и мы говорили, что Sketch должен вызывать в ответ на щелчок меню, вызываемый onRun ? (Если вам нужно освежить в памяти, просмотрите эту часть выше, а затем вернитесь.) Все, что делает этот бит, — это создает эту функцию. Вы также заметите, что наша функция onRun принимает аргумент context . Это аргумент, с которым Sketch будет вызывать ваши обработчики команд, которые могут предоставить нам определенную информацию. Позже мы будем использовать его, чтобы получить URL-адрес нашего пакета плагинов на компьютере пользователя.

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

Здесь мы на самом деле делаем несколько вещей:

  1. Во-первых, мы вызываем alloc() в NSWindow ; в основном это означает «отвести немного памяти для экземпляра NSWindow». Достаточно знать, что вам придется делать это для каждого экземпляра нативного класса, который вы хотите создать. Метод alloc доступен в каждом нативном классе.
  2. Затем мы вызываем метод инициализации NSWindow (то есть метод, который фактически создает экземпляр NSWindow ), который называется initWithContentRect:styleMask:backing:defer: . Вы заметите, что это отличается от того, что мы вызываем в нашем коде выше — у него есть куча двоеточий ( : ) между каждым аргументом. Поскольку мы не можем использовать этот синтаксис в JavaScript, Sketch удобно переименовывает его во что-то, что мы действительно можем использовать, заменяя двоеточия символами подчеркивания, так мы получаем его имя JS: initWithContentRect_styleMask_backing_defer .
  3. Затем мы передаем каждый из аргументов, необходимых методу. В качестве первого аргумента, contentRect , мы предоставляем прямоугольник достаточно большого размера для нашего пользовательского интерфейса.
  4. Для styleMask мы используем битовую маску, которая говорит, что мы хотим, чтобы наше окно имело кнопку закрытия, строку заголовка и изменяемый размер.
  5. Следующие два аргумента, backing и defer , всегда будут иметь значения NSBackingStoreBuffered и false , поэтому нам не нужно о них беспокоиться. (Документация для этого метода более подробно описывает, почему это так.)
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

Здесь мы устанавливаем для свойства releasedWhenClosed NSWindow значение false , что означает: «Эй! не удаляйте это окно из памяти только потому, что пользователь его закрывает». Затем мы вызываем makeKeyAndOrderFront (null) , что означает: «Переместите это окно на передний план и установите для него фокус клавиатуры».

Веб-представление: интерфейс

Чтобы упростить задачу, я уже написал код HTML и CSS пользовательского веб-интерфейса подключаемого модуля, который мы собираемся использовать; единственный оставшийся код, который нам нужно будет добавить, будет иметь дело с обеспечением связи между ним и нашим кодом плагина Sketch.

Затем загрузите код HTML и CSS. После того, как вы загрузили его, извлеките его, а затем переместите папку с именем «web-ui» в папку «Ресурсы» нашего плагина.

Примечание . Написание и оптимизация фактического кода HTML/CSS выходит за рамки этого руководства, поскольку основное внимание уделяется JavaScript, который поддерживает основные функции плагина; но в Интернете есть множество руководств по этой теме, если вы хотите узнать больше.

Если вы запустите наш плагин сейчас, вы увидите, что он показывает окно — ура, прогресс! Но он пустой, без названия и пока не особо полезный. Нам нужно, чтобы он отображал наш веб-интерфейс. Чтобы сделать это, нам понадобится другой собственный класс, WKWebView , который представляет собой представление, специально созданное для отображения веб-контента.

Мы добавим код, необходимый для создания нашего WKWebView под кодом, который мы написали для нашего окна:

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

Если мы сейчас запустим наш плагин, мы увидим, что теперь у нас открыто окно, в котором отображается наш веб-интерфейс пользователя. Успех!

Опять же, прежде чем двигаться дальше, давайте посмотрим, что делает добавленный нами код:

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

Это должно выглядеть знакомо — это в основном то же самое, что мы делали, когда создавали NSWindow : выделяем память для веб-представления, а затем инициализируем его.

 window.contentView = webView;

Эта строка кода указывает нашему окну отображать веб-представление, которое мы только что создали.

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

Здесь наша цель — создать URL-адрес, указывающий на папку web-ui , которую мы создали ранее. Чтобы получить этот URL-адрес, нам нужен какой-то способ выяснить, где находится пакет нашего плагина в файловой системе пользователя. Здесь мы используем свойство context.scriptURL , которое дает нам URL-адрес запущенного в данный момент скрипта . Однако это дает нам не String JavaScript, как можно было бы ожидать, а экземпляр собственного класса NSURL , в котором есть несколько методов, упрощающих манипулирование строками URL.

Нам нужно обратить то, что дает нам context.scriptURL

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

- в:

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

Шаг за шагом:

  1. Вызов URLByDeletingLastPathComponent() в первый раз дает нам file://path-to-your-plugin/Contents/Sketch/
  2. Вызов URLByDeletingLastPathComponent() снова дает нам file://path-to-your-plugin/Contents/
  3. И, наконец, добавление Resources/web-ui/ в конце с помощью URLByAppendingPathComponent ("Resources/web-ui/") дает нам file://path-to-your-plugin/Contents/Resources/web-ui/

Нам также нужно создать второй URL-адрес, который указывает непосредственно на файл index.html :

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

Наконец, мы говорим нашему веб-представлению загрузить index.html и предоставить ему доступ к содержимому папки web-ui :

 webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

Хорошо. На данный момент у нас есть окно, в котором отображается наш веб-интерфейс пользователя, как мы и хотели. Однако он еще не совсем завершен — в нашем исходном дизайне нет строки заголовка (или «хрома»), но в нашем текущем окне она есть. Также есть тот факт, что когда мы щелкаем внутри документа Sketch, этот документ перемещается перед нашим окном, а это не то, чего мы хотим — мы хотим, чтобы пользователь мог взаимодействовать с окном плагина и документом Sketch без необходимости постоянно перефокусироваться с одного окна на другое.

Чтобы исправить это, нам сначала нужно избавиться от хрома окна по умолчанию и оставить только кнопки. Добавление двух строк кода ниже избавит вас от строки заголовка.

Примечание. Как и раньше, все свойства и методы, которые мы используем ниже, задокументированы на странице документации NSWindow .

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

Следующие две строки кода удалят ненужные нам оконные кнопки (также известные как «светофоры» на жаргоне MacOS) — «масштаб» и «свернуть», — оставив только кнопку «закрыть»:

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

Пока мы этим занимаемся, давайте также изменим цвет фона окна, чтобы он соответствовал цвету нашего веб-интерфейса:

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

Затем нам нужно что-то сделать, чтобы наше плавающее окно плагина оставалось поверх других окон, чтобы пользователь мог взаимодействовать со своими документами Sketch, не беспокоясь об исчезновении окна Mosaic. Мы можем использовать для этого специальный тип NSWindow , называемый NSPanel , который способен «оставаться поверх» других окон. Все, что для этого нужно, — изменить NSWindow на NSPanel , что представляет собой однострочное изменение кода:

 const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Теперь мы говорим нашему окну панели плавать (оставаться поверх всех остальных) и фокусироваться на клавиатуре/мыши только при необходимости:

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

Мы также можем настроить наше окно так, чтобы оно автоматически открывалось в последней позиции, в которой оно было:

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

Эта строка в основном говорит: «запомните положение этого окна, сохранив его с настройками Sketch под ключевым mosaic-panel-frame ».

Все вместе, теперь у нас есть следующий код:

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

Организация Кодекса

Прежде чем мы перейдем к следующей части, рекомендуется организовать наш код так, чтобы в нем было легче ориентироваться и настраивать его. Так как нам нужно добавить еще много кода, и мы хотим, чтобы index.js не превратился в беспорядочную свалку для всего нашего кода, давайте немного разделим вещи и переместим код, специфичный для пользовательского интерфейса, в файл с именем ui.js в папке Sketch . Мы также выделим некоторые выполняемые нами задачи пользовательского интерфейса, такие как создание веб-представления и окна, в их собственные функции.

Создайте новый файл с именем ui.js и вставьте в него приведенный ниже код:

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

Здесь мы внесли несколько ключевых изменений, которые важно отметить. Помимо того факта, что мы создали специальные функции для создания, скрытия и отображения нашего окна и его веб-представления, мы также разбили наш код пользовательского интерфейса на модули.

Обратите внимание на module.exports = { loadAndShow, cleanup } внизу? Это способ для нас точно указать, какие объекты и функции могут использовать скрипты, которые импортируют этот код пользовательского интерфейса (и скрыть те, о которых мы не хотим, чтобы они беспокоились), что означает, что теперь у нас есть более организованный API для взаимодействия, показывать и уничтожать наш пользовательский интерфейс.

Рекомендуемое чтение : Раскрытие полного потенциала символов в Sketch

Давайте посмотрим, как это выглядит на практике. Вернитесь в index.js , удалите старый код и добавьте следующее:

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

Мы используем специальную функцию, которую Sketch автоматически предоставляет нам, require , для импорта нашего кода ui.js и присвоения возвращаемого модуля переменной UI . Это дает нам доступ к упрощенному API для запуска нашего пользовательского интерфейса. Теперь вещи намного опрятнее и их легко найти!

Заключение

Молодец — ты далеко зашел! 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!