Como construir um plugin de esboço com JavaScript, HTML e CSS (Parte 1)

Publicados: 2022-03-10
Resumo rápido ↬ Se você já trabalhou com o Sketch, as chances são de que houve muitos momentos em que você pensou: “Se apenas o Sketch pudesse fazer uma coisa em particular, eu seria capaz de realizar a tarefa em mãos muito mais rápido, fácil e melhor.” Bem, não se preocupe mais! Neste artigo de duas partes, você aprenderá como construir seus próprios plugins do Sketch do zero — dando a você as habilidades necessárias para resolver exatamente esses tipos de problemas.

Este tutorial é destinado a pessoas que conhecem e usam o aplicativo Sketch e não têm medo de mexer com código. Para lucrar mais com isso, você precisará ter pelo menos alguma experiência básica escrevendo JavaScript (e, opcionalmente, HTML/CSS).

O plugin que vamos criar chama-se “Mosaic”. Na primeira parte, aprenderemos sobre os arquivos básicos que compõem um plugin do Sketch; escreveremos um pouco de JavaScript e criaremos uma interface de usuário para nosso plugin com a ajuda de HTML e CSS. O próximo artigo será sobre como conectar a interface do usuário ao código do plug-in principal, como implementar os principais recursos do plug-in e, ao final, você também aprenderá a otimizar o código e a maneira como o plug-in funciona.

Também compartilharei o código do plugin (JS, HTML, CSS) e arquivos que você poderá examinar e usar para fins de aprendizado.

O que são plug-ins de esboço e como eles funcionam?

No Sketch, os plug-ins são uma maneira de adicionar recursos e funcionalidades que não estão presentes no Sketch “prontas para uso”. Considerando que quase sempre haverá algum recurso ou integração ausente em qualquer programa (especialmente devido ao grande número de necessidades que qualquer designer individual pode ter!), pode-se começar a imaginar como os plugins podem ser especialmente úteis e poderosos. Os plugins de esboço são capazes de fazer praticamente tudo o que você espera, como manipular a cor, forma, tamanho, ordem, estilo, agrupamento e efeitos de camadas, mas também podem fazer coisas como fazer solicitações a recursos da Internet, apresentar um usuário interface e muito, muito mais!

No lado da programação, todos os plugins do Sketch são escritos em código JavaScript. Bem, na verdade, isso não é inteiramente verdade. É mais correto dizer que a maioria dos plug-ins do Sketch são escritos em JavaScript, pois também é possível escrever um plug-in do Sketch em uma das linguagens de programação da Apple, Objective-C e Swift, embora mesmo eles exijam uma pequena quantidade de conhecimento em JavaScript.

Não se preocupe. Neste artigo, vamos nos concentrar em como criar plug-ins do Sketch usando apenas JavaScript, HTML e CSS . Não abordaremos o básico de HTML, CSS ou JavaScript — este artigo pressupõe pelo menos algum conhecimento e experiência com todos esses três. O site do desenvolvedor MDN oferece um ótimo lugar para aprender mais sobre desenvolvimento web.

Mais depois do salto! Continue lendo abaixo ↓

Vamos começar!

Em primeiro lugar, o que estamos fazendo?

Neste tutorial, ensinarei como construir um plugin básico e amigável para iniciantes que será capaz de criar, duplicar e modificar camadas, bem como apresentar ao usuário uma interface de usuário agradável. Ao fazer isso, meu objetivo é estabelecer um conhecimento fundamental sobre o qual você pode construir e usá-lo para criar seus próprios plugins.

O plugin que vamos construir chama-se Mosaic, e é efetivamente um “gerador de padrões”. Alimente-o com suas camadas, ajuste algumas configurações e ele criará um padrão:

Imagem mostrando a interface do usuário do plugin Mosaic e alguns padrões de exemplo.
A interface do usuário do Mosaic e alguns exemplos de padrões feitos com ele. (Visualização grande)

Se você quiser instalar e brincar com o Mosaic, você pode baixar o plugin completo do GitHub.

Um pouco de história: o Mosaic é inspirado em grande parte por um plug-in antigo do Adobe Fireworks chamado Twist-and-Fade . Twist-and-Fade era bastante poderoso, capaz de duplicar uma camada várias vezes enquanto ajustava sua tonalidade, posição, rotação, tamanho e opacidade. O plugin conseguiu até gerar GIFs animados, como este, onde criava os frames para os dois elementos giratórios da fita cassete:

Imagem mostrando uma fita cassete de música com tambores giratórios
Fita cassete animada (fonte). (Visualização grande)

(Aqui está um vídeo demonstrando Twist and Fade se você estiver interessado em ver exatamente como funcionou.)

Para os propósitos deste tutorial, estaremos construindo um plugin um pouco semelhante para o Sketch, embora intencionalmente simplificado para manter o tutorial o mais acessível possível. Especificamente, nosso plugin será capaz de:

  • Duplique qualquer camada do Sketch (bitmap ou vetor) e ajuste a posição, rotação e opacidade da camada das duplicatas. Isso nos dará uma introdução à manipulação de camadas usando as APIs JavaScript do Sketch.
  • Exiba uma interface de usuário criada usando HTML, CSS e JS, que ensinará como criar facilmente uma interface para o plug-in, usando tecnologias da Web com as quais você já deve estar familiarizado. A interface do plugin é muito importante, pois é como coletaremos as entradas do usuário sobre como o usuário deseja que a imagem resultante do mosaico seja.

Criando nosso plug-in base em dez segundos

Primeiro, vamos criar a “base” (ou template) para o plugin que queremos construir. Poderíamos criar manualmente todos os arquivos e pastas necessários que compõem um plugin, mas felizmente não precisamos — porque o Sketch pode fazer isso por nós. Depois de gerarmos o plug-in de modelo, poderemos personalizá-lo como acharmos melhor.

Há uma técnica muito rápida e fácil que podemos usar para criar o plugin de modelo, que é praticamente meu método quando preciso juntar um plugin para resolver qualquer problema com o qual estou lidando em um determinado momento. Veja como funciona:

Com o Sketch aberto, verifique a barra de menu na parte superior da tela e clique em Plugins -> Run Script . Isso abrirá uma caixa de diálogo que podemos usar para testar e executar o código. Também podemos salvar qualquer código inserido nele como um plug-in, que é a parte em que estamos especificamente interessados ​​no momento.

Limpe qualquer código que já esteja nesta caixa de diálogo e substitua-o pelo seguinte código de demonstração:

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

Em seguida, clique em Save Script as Plugin -in no canto inferior esquerdo da janela, digite o nome que você gostaria que este plug-in tivesse (no nosso caso, este é “Mosaico”) e, em seguida, Save Script as Plugin mais uma vez.

Pressione “Salvar” no canto inferior esquerdo da janela e digite o nome que você gostaria que este plugin tivesse. (Visualização grande)

Acredite ou não, já terminamos - tudo o que resta é comer o bolo que acabamos de fazer. Aqui vem a parte divertida. Abrindo o menu Plugins mais uma vez, você deve ver algo assim: seu plugin novinho em folha listado como “Mosaic”! Clique nisso!

(Visualização grande)

Parabéns, você acabou de escrever seu primeiro plugin do Sketch!

O que você deve ver depois de clicar em “Mosaico” deve ser como o pequeno vídeo acima, com uma mensagem de dica de ferramenta discreta aparecendo na parte inferior da tela começando com as palavras “Ei…” – que é exatamente o que o código que colamos diz. façam. É isso que torna essa técnica tão boa: ela facilita colar, modificar e testar o código sem ter que construir um plugin do zero. Se você está familiarizado ou já jogou com o console web do seu navegador, é basicamente isso. Ter essa ferramenta em seu bolso enquanto você cria e testa o código é essencial.

Vamos fazer um resumo rápido do que o código que você adicionou faz:

Primeiro, ele importa o módulo sketch/ui da biblioteca JS integrada do Sketch e o atribui à variável UI . Este módulo contém alguns métodos úteis relacionados à interface, um dos quais usaremos:

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

Em seguida, ele chama o método message (que faz parte do módulo sketch/ui ) com a string de texto que queremos exibir na dica de ferramenta que vimos:

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

O método message() fornece uma ótima maneira de apresentar uma mensagem discreta ao usuário; é ótimo para casos em que você não precisa roubar o foco (não modal) e não precisa de botões ou campos de texto sofisticados. Há também outras maneiras de apresentar elementos comuns da interface do usuário, como alertas, prompts e outros, alguns dos quais usaremos à medida que construímos o Mosaic.

Personalizando os metadados do nosso plug-in

Agora temos um plugin básico para começar, mas ainda precisamos ajustá-lo ainda mais e torná-lo verdadeiramente nosso. Nosso próximo passo será alterar os metadados do plugin.

Para esta etapa, precisaremos dar uma olhada no que é chamado de pacote de plug -ins . Quando você clica em salvar na janela 'Run Script', o Sketch salva seu plugin como uma pasta chamada Mosaic.sketchplugin que você pode encontrar no diretório ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins . Isso é um pouco longo e chato de lembrar; como um atalho, você também pode acessá-lo via Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder . Mesmo que apareça no Finder como um único arquivo, na verdade é uma pasta que contém tudo o que nosso plugin precisa para que o Sketch o execute. A razão pela qual ele aparece como um único arquivo, apesar de ser uma pasta, é porque quando você instalou o Sketch pela primeira vez, o Sketch registrou a extensão .sketchplugin como um “pacote” (um tipo especial de pasta que aparece como um arquivo) e pediu para abrir automaticamente no Sketch quando aberto.

Vamos dar uma olhada lá dentro. Clique com o botão direito do mouse em Mosaic.sketchplugin e clique em “Mostrar conteúdo do pacote”. Dentro, você deve ver a seguinte estrutura de diretórios:

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

Você pode estar se perguntando por que existe um arquivo com a extensão .cocoascript . Não se preocupe — é apenas um arquivo JavaScript normal e contém apenas o código que inserimos anteriormente. Vá em frente e renomeie este arquivo para index.js , o que mudará a estrutura do diretório para se parecer com a abaixo:

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

A maneira mais comum de organizar os arquivos dentro de um pacote de plugins é a seguinte: seu código (JavaScript) e manifest.json pertencem ao Sketch/ , e os recursos (pense em imagens, arquivos de áudio, arquivos de texto, etc.) pertencem a Resources/ .

Vamos começar ajustando o arquivo chamado manifest.json . Abra-o dentro do seu editor de código favorito, como Visual Studio Code ou Atom.

Você verá que no momento há relativamente pouco aqui, mas adicionaremos mais em breve. O manifesto do plug-in serve principalmente a dois propósitos:

  1. Primeiro, ele fornece metadados que descrevem o plug-in para o usuário – coisas como nome, versão, nome do autor e assim por diante. O Sketch usa essas informações na caixa de diálogo Sketch -> Preferences -> Plugins para criar uma lista e descrição para seu plugin.
  2. Em segundo lugar, também informa ao Sketch sobre como chegar ao seu negócio; isto é, ele diz ao Sketch como você gostaria que o menu do seu plugin fosse, quais teclas de atalho atribuir ao seu plugin e onde o código do seu plugin fica (para que o Sketch possa executá-lo).

Considerando o propósito nº 1, descrevendo o plugin para o usuário, você provavelmente notará que agora não há descrição ou autor fornecido, o que seria confuso para o usuário e dificultaria a identificação do plugin. Vamos corrigir isso ajustando os valores das chaves relevantes para:

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

Em seguida, vamos ajustar o identificador do plugin. Esse identificador usa o que é chamado de “notação de domínio reverso”, que é uma maneira realmente concisa (ou chata, escolha a sua escolha) de dizer “pegue o domínio do seu site, inverta a ordem e coloque o nome do seu produto no final”. Isso será algo como: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct .

Você não precisa seguir essa convenção de nomenclatura - você pode colocar o que quiser aqui, desde que seja exclusivo o suficiente para evitar conflitos com outros plugins (embora seja provavelmente uma boa ideia manter o formato RDN, especialmente porque ele fornece um sistema simples e reutilizável para seus identificadores de plugin).

Para isso, altere seu identificador para com.your-name.mosaic :

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

Eu pessoalmente gosto de pegar todas as chaves relacionadas a metadados (título, autor, identificador, etc.) .

Em seguida, vamos dar uma olhada no menu e nas teclas de commands . Esses dois são responsáveis ​​por dizer ao Sketch qual código chamar e em resposta a quê.

Se você olhar para a chave do menu , verá que ela contém uma chave de title , cujo valor é o nome com o qual nosso plugin aparecerá no menu Plugins . Ele também possui uma chave de items , que é uma lista de identificadores de comando :

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

No momento, há apenas um identificador de comando nesta lista, "com.bohemiancoding.sketch.runscriptidentifier" . Os identificadores de comando sempre apontam para um comando na lista de commands . No momento nosso plugin tem apenas um comando, que é aquele com este identificador:

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

Sempre que você adicionar um identificador de comando a uma entrada de menu , o Sketch procurará a entrada de comando que possui esse identificador e exibirá o valor de sua chave de name (que neste caso é "Mosaico") e a mostrará no menu do seu plug-in do identificador.

Quanto aos comandos de função, podemos pensar em uma entrada de comando como uma maneira de dizer ao Sketch qual função no código JavaScript do nosso plugin queremos executar quando esse comando é invocado, a “invocação” geralmente sendo o clique do usuário no menu associado item. A entrada de comando não faz nada por conta própria, é apenas JSON — ela simplesmente fornece uma descrição para o Sketch de onde procurar o JavaScript que precisa ser executado quando o comando é invocado.

Até agora, falamos sobre o que o name de um comando e as chaves identifier fazem, mas há duas outras chaves em um comando que precisam ser endereçadas: script e handlers .

A chave de script informa ao Sketch onde está o arquivo JavaScript que deve ser executado. Observe como o Sketch assume que o arquivo de script em questão está na pasta Sketch/ , e é por isso que, para simplificar, você deve certificar-se de que todo o seu código JavaScript esteja em algum lugar na pasta Sketch/ . Antes de passarmos dessa chave , é importante que você altere o valor dessa chave para index.js , assim como renomeamos o arquivo anteriormente. Caso contrário, o Sketch não poderá encontrar e executar seu arquivo JavaScript.

O valor da chave de handlers é o que o Sketch analisa para determinar qual função em seu JavaScript deve ser chamada. Aqui, temos apenas um conjunto de manipuladores: run , com o valor onRun . run é o nome de uma ação de Sketch integrada e predefinida. Essa ação run sempre será chamada quando um usuário clicar em um item de menu que faça referência a esse comando. onRun é o nome de uma função no arquivo script.cocoascript gerado automaticamente (que renomeamos para index.js ), e a função que queremos que seja chamada quando ocorrer o evento run , ou seja, quando o usuário clicar no item de menu.

No exemplo que temos até agora, esse processo funciona mais ou menos assim:

  1. O usuário clica em nosso item de menu.
  2. O Sketch encontra o comando associado a esse item de menu.
  3. O Sketch encontra o arquivo de script ao qual o comando se refere e o executa (o que neste caso significa que ele executa o JavaScript em index.js ).
  4. Como esse comando foi invocado por um clique em um item de menu, ele é considerado uma ação de run . Isso significa que o Sketch examinará o valor handlers.run do comando para a função a ser chamada em seguida, que neste caso é onRun .
  5. Sketch chama a função onRun .

Os comandos são mais comumente chamados em resposta a um usuário clicando em um de seus itens de menu, mas também podem ser chamados em resposta a outras ações do usuário, como o usuário alterar a seleção ou uma propriedade em uma camada. No entanto, para este plugin, não usaremos nenhuma dessas outras ações. (Você pode saber mais sobre ações e como elas funcionam na página de ajuda da API Action.)

Antes de passarmos deste manifesto, faremos outros dois ajustes. Neste momento, nosso cardápio tem a estrutura:

 Mosaic └ Mosaic 
Imagem mostrando o item de menu Mosaic aninhado de forma redundante dentro de outro menu chamado Mosaic
Bem redundante, certo? (Visualização grande)

…o que é um pouco redundante já que nosso plugin tem apenas um item de menu. Também adiciona um pouco de atrito desnecessário para nosso usuário, pois nosso plugin agora leva dois cliques para invocar em vez de um. Podemos corrigir isso adicionando isRoot: true ao nosso menu :

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

Isso diz ao Sketch para colocar o primeiro nível de itens de menu diretamente no menu Plugins , em vez de aninha-los sob o title do menu.

Clique em salvar e retorne ao Sketch. Você deve ver que agora Mosaic -> Mosaic foi substituído apenas por Mosaic - perfeito!

Imagem mostrando a interface do usuário do plug-in Mosaic
IU do Mosaic. (Visualização grande)

Quanto ao nosso segundo ajuste, vamos em frente e renomeie este identificador de comando para algo menos complicado. Como os identificadores de comando só precisam ser exclusivos no contexto de um plug-in individual, podemos renomeá-lo com segurança para algo mais conciso e óbvio, como "open" :

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

Antes de prosseguirmos, é útil observar que os menus também podem conter outros menus. Você pode facilmente criar um submenu aninhando outra entrada { title: ..., items: ... } dentro da lista de items de outro menu:

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

Construindo a interface de usuário do plugin

Até agora, escrevemos alguns códigos de demonstração e personalizamos o manifesto do nosso plugin. Passaremos agora para a criação de sua interface de usuário, que é essencialmente uma página da Web incorporada em uma janela (de maneira semelhante aos navegadores com os quais você está familiarizado):

A janela do plugin. (Visualização grande)
Imagem mostrando os componentes que compõem a interface do nosso plugin: janela e visualização web
Os componentes que compõem nosso plugin. (Visualização grande)

A janela

O design da interface de usuário do Mosaic tem sua própria janela, que podemos considerar o componente mais básico; vamos começar com ele. Para criar e exibir uma janela, teremos que usar uma classe incorporada ao macOS por padrão, chamada NSWindow . No restante deste tutorial, faremos isso um pouco (usando APIs integradas como NSWindow ), o que pode parecer um pouco assustador se você não estiver familiarizado com isso, mas não se preocupe - vou explicar tudo pelo caminho!

Nota: Enquanto estamos falando de APIs integradas, a razão pela qual podemos usar essa classe é graças a uma ponte presente no tempo de execução do JavaScript usado pelos plug-ins do Sketch. Essa ponte importa automaticamente essas classes, métodos e funções integrados que normalmente estariam disponíveis apenas para aplicativos nativos.

Abra Sketch/index.js em seu editor de código, exclua o que já está lá e cole o seguinte:

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

Vamos dar uma olhada no que esse primeiro pedaço de código faz:

 function onRun(context){

Lembra-se anteriormente quando falamos sobre comandos e como eles funcionam, e dissemos ao Sketch para chamar em resposta a um clique de menu chamado onRun ? (Se você precisar de uma atualização, revisite essa parte acima e depois volte.) Tudo o que esse bit faz é criar essa função. Você também notará que nossa função onRun recebe um argumento de context . Este é um argumento que o Sketch chamará seus manipuladores de comando que podem nos fornecer certas informações. Mais tarde, vamos usá-lo para obter a URL do nosso pacote de plugins no computador do usuário.

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

Aqui estamos realmente fazendo algumas coisas:

  1. Primeiro, chamamos alloc() em NSWindow ; isso basicamente significa “reserve um pouco de memória para uma instância do NSWindow”. É suficiente saber que você terá que fazer isso para cada instância de uma classe nativa que deseja criar. O método alloc está disponível em todas as classes nativas.
  2. Em seguida, chamamos o método inicializador de NSWindow (ou seja, o método que realmente cria uma instância de NSWindow ), que é denominado initWithContentRect:styleMask:backing:defer: . Você notará que é diferente do que chamamos em nosso código acima — tem vários dois-pontos ( : ) entre cada argumento. Como não podemos usar essa sintaxe em JavaScript, o Sketch a renomeia convenientemente para algo que podemos usar substituindo os dois pontos por sublinhados, que é como obtemos seu nome JS: initWithContentRect_styleMask_backing_defer .
  3. Em seguida, passamos cada um dos argumentos que o método precisa. Para o primeiro argumento, contentRect , fornecemos um retângulo com tamanho grande o suficiente para nossa interface de usuário.
  4. Para styleMask , usamos uma máscara de bits que diz que queremos que nossa janela tenha um botão de fechar, uma barra de título e que seja redimensionável.
  5. Os próximos dois argumentos, backing e defer , sempre serão definidos como NSBackingStoreBuffered e false , portanto, não precisamos nos preocupar com eles. (A documentação para este método entra em mais detalhes sobre o motivo disso.)
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

Aqui definimos a propriedade releasedWhenClosed do NSWindow como false , o que significa: “Ei! não exclua esta janela da memória apenas porque o usuário a fechou.” Em seguida, chamamos makeKeyAndOrderFront (null) que significa: “Mova esta janela para a frente e dê foco ao teclado”.

Visualização da Web: A Interface

Para facilitar as coisas, já escrevi o código HTML e CSS da interface de usuário da web do plugin que vamos usar; o único código restante que teremos que adicionar a ele tratará de garantir que possamos nos comunicar entre ele e nosso código de plug-in do Sketch.

Em seguida, baixe o código HTML e CSS. Depois de baixá-lo, extraia-o e mova a pasta chamada “web-ui” para a pasta Resources do nosso plugin.

Nota : Escrever e otimizar o código HTML/CSS real está fora do escopo deste tutorial, pois seu foco está no JavaScript, que alimenta os recursos principais do plug-in; mas há uma tonelada de tutoriais na web sobre este tópico, caso você queira aprender mais.

Se você executar nosso plugin agora, verá que ele mostra uma janela — yay, progresso! Mas está vazio, sem título, e ainda não é particularmente útil. Precisamos fazer com que ele mostre nossa interface da web. Para fazer isso, precisaremos usar outra classe nativa, WKWebView , que é uma visualização feita especificamente para exibir conteúdo da web.

Adicionaremos o código necessário para criar nosso WKWebView abaixo do código que escrevemos para nossa janela:

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

Se executarmos nosso plugin agora, veremos que agora temos uma janela aberta que exibe nossa interface de usuário da web. Sucesso!

Novamente, antes de prosseguir, vamos examinar o que o código que adicionamos faz:

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

Isso deve parecer familiar - é basicamente o mesmo que fizemos quando criamos nosso NSWindow : alocar memória para uma visualização da Web e inicializá-lo.

 window.contentView = webView;

Esta linha de código diz à nossa janela para exibir a visualização da web que acabamos de criar.

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

Aqui nosso objetivo é criar uma URL que aponte para a pasta web-ui que criamos anteriormente. Para obter essa URL, precisamos de alguma forma de descobrir onde o pacote do nosso plugin está no sistema de arquivos do usuário. Aqui usamos a propriedade context.scriptURL , que nos dá a URL do script atualmente em execução . No entanto, isso não nos fornece uma String JavaScript como você poderia esperar, mas uma instância de uma classe nativa, NSURL , que possui alguns métodos que facilitam a manipulação de strings de URL.

Precisamos transformar o que context.scriptURL nos dá —

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

- para dentro:

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

Passo a passo:

  1. Chamar URLByDeletingLastPathComponent() pela primeira vez nos dá file://path-to-your-plugin/Contents/Sketch/
  2. Chamar URLByDeletingLastPathComponent() novamente nos dá file://path-to-your-plugin/Contents/
  3. E, por último, adicionar Resources/web-ui/ no final usando URLByAppendingPathComponent ("Resources/web-ui/") nos dá file://path-to-your-plugin/Contents/Resources/web-ui/

Também precisamos criar uma segunda URL que aponte diretamente para o arquivo index.html :

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

Por fim, dizemos à nossa visualização da web para carregar index.html e dar acesso ao conteúdo da pasta web-ui :

 webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);

Tudo bem. Até agora, temos uma janela que exibe nossa interface de usuário da web, exatamente como queríamos. No entanto, ainda não está completo — nosso design original não tem uma barra de título (ou “cromo”), mas nossa janela atual tem. Há também o fato de que, quando clicamos dentro de um documento do Sketch, esse documento se move na frente da nossa janela, o que não é o que queremos - queremos que o usuário possa interagir com a janela do plugin e o documento do Sketch sem ter que constantemente refocalizar de uma janela para outra.

Para corrigir isso, primeiro precisamos nos livrar do cromo da janela padrão e manter apenas os botões. Adicionar as duas linhas de código abaixo eliminará a barra de título.

Nota: Como antes, todas as propriedades e métodos que estamos usando abaixo estão documentados na página de documentação do NSWindow .

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

Essas próximas duas linhas de código removerão os botões da janela (também conhecidos como “semáforos” no jargão do MacOS) que não precisamos – “zoom” e “minimize” – deixando apenas o botão “fechar”:

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

Enquanto estamos nisso, vamos também alterar a cor de fundo da janela para corresponder à nossa interface da web:

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

Em seguida, precisamos fazer algo para manter nossa janela de plugin flutuante em cima de outras janelas, para que o usuário possa interagir com seus documentos do Sketch sem ter que se preocupar com o desaparecimento da janela do Mosaic. Podemos usar um tipo especial de NSWindow para isso, chamado NSPanel , que é capaz de “ficar em cima” de outras janelas. Tudo o que é necessário para isso é alterar NSWindow para NSPanel , que é uma alteração de código de linha única:

 const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(

Agora dizemos à nossa janela do painel para flutuar (ficar no topo de todas as outras) e apenas focar o teclado/mouse quando necessário:

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

Também podemos ajustar nossa janela para que ela reabra automaticamente na última posição em que estava:

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

Esta linha basicamente diz “lembre-se da posição desta janela salvando-a com as preferências do Sketch sob a chave mosaic-panel-frame ”.

Todos juntos, temos agora o seguinte código:

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

Organizando o código

Antes de passarmos para a próxima parte, é uma boa ideia organizar nosso código para que seja mais fácil navegar e ajustar. Como ainda temos muito mais código para adicionar e queremos evitar que index.js se torne um depósito de lixo confuso para todo o nosso código, vamos dividir um pouco as coisas e mover nosso código específico da interface do usuário para um arquivo chamado ui.js , na pasta Sketch . Também extrairemos algumas das tarefas de interface do usuário que fazemos, como criar a visualização da Web e a janela, em suas próprias funções.

Crie um novo arquivo chamado ui.js e insira o código abaixo dentro dele:

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

Há algumas mudanças importantes que fizemos aqui que são importantes notar. Além do fato de termos criado funções específicas para a criação, ocultação e exibição de nossa janela e sua visualização web, também modularizamos nosso código de interface de usuário.

Observe a module.exports = { loadAndShow, cleanup } na parte inferior? Essa é uma maneira de especificarmos exatamente quais objetos e funções os scripts que importam esse código de interface do usuário podem usar (e ocultar aqueles com os quais não queremos que eles se preocupem), o que significa que agora temos uma API mais organizada para interagir, mostrando e destruindo nossa interface do usuário.

Leitura recomendada : Liberando todo o potencial dos símbolos no esboço

Vamos ver como é isso na prática. De volta ao index.js , remova o código antigo e adicione o seguinte:

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

Estamos usando uma função especial que o Sketch disponibiliza automaticamente para nós, require , para importar nosso código ui.js e atribuir o módulo retornado à variável UI . Isso nos dá acesso a uma API simplificada para acionar nossa interface de usuário. As coisas estão muito mais organizadas agora e fáceis de encontrar!

Conclusão

Muito bem - você chegou longe! 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!