كيفية إنشاء البرنامج المساعد Sketch باستخدام JavaScript و HTML و CSS (الجزء الأول)

نشرت: 2022-03-10
ملخص سريع ↬ إذا سبق لك العمل مع Sketch ، فالاحتمالات هي أن هناك الكثير من اللحظات التي فكرت فيها ، "إذا كان بإمكان Sketch فقط القيام بهذا الشيء المعين ، فسأكون قادرًا على إنجاز المهمة التي بين يديك أسرع بكثير ، وأسهل ، وأفضل ". حسنًا ، لم يعد القلق! في هذه المقالة المكونة من جزأين ، ستتعلم كيفية إنشاء مكونات Sketch الإضافية الخاصة بك من البداية - مما يمنحك المهارات اللازمة لحل هذه الأنواع من المشكلات بالضبط.

هذا البرنامج التعليمي مخصص للأشخاص الذين يعرفون ويستخدمون تطبيق Sketch ولا يخشون الانغماس في الكود. للاستفادة منها أكثر من غيرها ، ستحتاج على الأقل إلى بعض الخبرة الأساسية في كتابة JavaScript (واختياريًا ، HTML / CSS).

يُطلق على المكون الإضافي الذي سننشئه اسم "Mosaic". في الجزء الأول ، سنتعرف على الملفات الأساسية التي يتكون منها المكون الإضافي Sketch ؛ سنقوم بكتابة بعض JavaScript وإنشاء واجهة مستخدم للمكون الإضافي الخاص بنا بمساعدة بعض HTML و CSS. ستكون المقالة التالية حول كيفية توصيل واجهة المستخدم برمز المكون الإضافي الأساسي ، وكيفية تنفيذ الميزات الرئيسية للمكون الإضافي ، وفي نهايته ، ستتعلم أيضًا كيفية تحسين الكود وطريقة عمل المكون الإضافي.

سأقوم أيضًا بمشاركة كود المكون الإضافي (JS و HTML و CSS) والملفات التي ستتمكن من فحصها واستخدامها لأغراض التعلم.

ما هي ملحقات الرسم وكيف تعمل؟

في 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.

القليل من التاريخ: مستوحى الفسيفساء في جزء كبير منه من البرنامج الإضافي Adobe Fireworks للمدرسة القديمة يسمى Twist-and-Fade . كان Twist-and-Fade قويًا جدًا ، حيث كان قادرًا على تكرار طبقة أي عدد من المرات أثناء ضبط تدرج اللون والموضع والدوران والحجم والعتامة. كان المكوّن الإضافي قادرًا على إنشاء صور GIF متحركة ، مثل هذه ، حيث أنشأ الإطارات للعنصرين الدوّارين في شريط الكاسيت:

صورة تظهر شريط كاسيت موسيقي مع طبول دوارة
شريط الكاسيت المتحرك (المصدر). (معاينة كبيرة)

(إليك مقطع فيديو يعرض Twist and Fade إذا كنت مهتمًا برؤية كيفية عملها بالضبط.)

لأغراض هذا البرنامج التعليمي ، سننشئ مكونًا إضافيًا مشابهًا إلى حد ما لـ Sketch ، على الرغم من أنه تم تبسيطه عن قصد للحفاظ على إمكانية الوصول إلى البرنامج التعليمي قدر الإمكان. على وجه التحديد ، سيكون المكون الإضافي الخاص بنا قادرًا على:

  • قم بتكرار أي طبقة رسم (صورة نقطية أو متجه) وقم بتعديل موضع طبقة التكرارات ودورانها وشفافيتها. سيعطينا هذا مقدمة لمعالجة الطبقات باستخدام واجهات برمجة تطبيقات JavaScript في 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 في الجزء السفلي الأيسر من النافذة ، وأدخل الاسم الذي تريده لهذا المكون الإضافي (في حالتنا ، هذا هو "Mosaic") ، ثم Save Script as Plugin مرة أخرى.

اضغط على "حفظ" في الجزء السفلي الأيسر من النافذة وأدخل أي اسم تريده لهذا المكون الإضافي. (معاينة كبيرة)

صدق أو لا تصدق ، لقد انتهينا بالفعل - كل ما تبقى هو أكل الكعكة التي خبزناها للتو. هنا يأتي الجزء الممتع. عند فتح قائمة الإضافات مرة أخرى ، يجب أن ترى شيئًا كهذا: المكون الإضافي الجديد للعلامة التجارية الخاص بك مدرج باسم "Mosaic"! انقر عليه!

(معاينة كبيرة)

تهانينا ، لقد كتبت للتو أول مكون إضافي لبرنامج Sketch!

ما يجب أن تراه بعد النقر فوق "Mosaic" يجب أن يكون مثل الفيديو القصير أعلاه ، مع ظهور رسالة تلميح أداة غير مزعجة أسفل الشاشة تبدأ بالكلمات "مرحبًا هناك ..." - وهو بالضبط ما تخبره الشفرة التي تم لصقها بها لكى يفعل. هذا ما يجعل هذه التقنية رائعة للغاية: فهي تجعل من السهل لصق وتعديل واختبار الكود دون الحاجة إلى إنشاء مكون إضافي من البداية. إذا كنت معتادًا على وحدة تحكم الويب الخاصة بالمستعرض أو سبق لك اللعب بها ، فهذا هو الأساس. يعد وجود هذه الأداة في جيبك الخلفي أثناء إنشاء الكود واختباره أمرًا ضروريًا.

لنقم باستعراض سريع لما يفعله الكود الذي أضفته:

أولاً ، يستورد وحدة 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() طريقة رائعة لتقديم رسالة غير مزعجة للمستخدم ؛ إنه رائع للحالات التي لا تحتاج فيها إلى سرقة التركيز (غير مشروط) ولا تحتاج إلى أي أزرار أو حقول نصية رائعة. هناك أيضًا طرق أخرى لتقديم عناصر واجهة المستخدم الشائعة مثل التنبيهات والمطالبات وما شابه ، والتي سنستخدم بعضها أثناء إنشاء الفسيفساء.

تخصيص البيانات الوصفية للبرنامج المساعد الخاص بنا

لدينا الآن مكون إضافي أساسي نبدأ منه ، لكننا ما زلنا بحاجة إلى تعديله بشكل أكبر وجعله حقًا لنا. ستكون خطوتنا التالية هي تغيير البيانات الوصفية للملحق.

في هذه الخطوة ، سنحتاج إلى إلقاء نظرة خاطفة على ما يسمى بحزمة البرنامج المساعد . عندما تضغط على حفظ في نافذة "تشغيل البرنامج النصي" ، قام 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/ folder ، ولهذا السبب من أجل البساطة ، سترغب في التأكد من أن كل كود JavaScript موجود في مكان ما تحت Sketch/ المجلد. قبل الانتقال من هذا المفتاح ، من المهم أن تتأكد من تغيير قيمة هذا المفتاح إلى index.js ، تمامًا كما قمنا بإعادة تسمية الملف سابقًا. بخلاف ذلك ، لن يتمكن Sketch من العثور على ملف JavaScript وتشغيله.

إن قيمة مفتاح handlers هي ما ينظر إليه سكتش لتحديد الوظيفة في جافا سكريبت لديك لاستدعاءها. هنا ، لدينا مجموعة معالج واحد فقط: run ، مع القيمة onRun . run هو اسم إجراء Sketch محدد مسبقًا ومضمَّن. سيتم استدعاء إجراء run هذا دائمًا عندما ينقر المستخدم فوق عنصر قائمة يشير إلى هذا الأمر. onRun هو اسم وظيفة في ملف script.cocoascript الذي تم إنشاؤه تلقائيًا (والذي قمنا بإعادة تسميته إلى index.js ) ، والوظيفة التي نريد أن نطلق عليها عند حدوث حدث run ، أي عندما ينقر المستخدم على عنصر القائمة.

في المثال الذي لدينا حتى الآن ، تلعب هذه العملية شيئًا كالتالي:

  1. ينقر المستخدم فوق عنصر القائمة لدينا.
  2. يعثر Sketch على الأمر المرتبط بعنصر القائمة هذا.
  3. يعثر Sketch على ملف البرنامج النصي الذي يشير إليه الأمر ويقوم بتشغيله (مما يعني في هذه الحالة أنه ينفذ JavaScript في index.js ).
  4. نظرًا لاستدعاء هذا الأمر عن طريق النقر فوق عنصر القائمة ، فإنه يعتبر إجراء run . هذا يعني أن سكتش سوف ينظر في handlers.run الأوامر.قيمة التشغيل للدالة التي يجب استدعاءها بعد ذلك ، والتي في هذه الحالة هي onRun .
  5. يستدعي Sketch وظيفة onRun .

يتم استدعاء الأوامر بشكل شائع استجابةً لنقر المستخدم على أحد عناصر القائمة الخاصة بك ، ولكن يمكن أيضًا استدعاؤها استجابة لإجراءات المستخدم الأخرى ، مثل تغيير المستخدم للتحديد أو خاصية على طبقة. ومع ذلك ، بالنسبة لهذا المكون الإضافي ، لن نستخدم أيًا من هذه الإجراءات الأخرى. (يمكنك معرفة المزيد حول الإجراءات وكيفية عملها في صفحة تعليمات Action API.)

قبل أن ننتقل من هذا البيان ، سنرغب في إجراء تعديلين آخرين. في الوقت الحالي ، تحتوي قائمتنا على الهيكل:

 Mosaic └ Mosaic 
صورة تعرض عنصر قائمة Mosaic متداخلاً بشكل متكرر داخل قائمة أخرى تسمى Mosaic
زائدة عن الحاجة ، أليس كذلك؟ (معاينة كبيرة)

... وهي زائدة عن الحاجة إلى حد ما نظرًا لأن المكون الإضافي الخاص بنا يحتوي على عنصر قائمة واحد فقط. كما أنه يضيف القليل من الاحتكاك غير الضروري لمستخدمنا لأن المكون الإضافي الخاص بنا يستغرق الآن نقرتين للاستدعاء بدلاً من نقرتين. يمكننا إصلاح هذا عن طريق إضافة isRoot: true إلى menu :

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

يخبر هذا سكتش بوضع المستوى الأول من عناصر القائمة مباشرة ضمن قائمة 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 . خلال الفترة المتبقية من هذا البرنامج التعليمي ، سنفعل ذلك قليلاً (باستخدام واجهات برمجة التطبيقات المضمنة مثل NSWindow ) والذي قد يبدو شاقًا بعض الشيء إذا لم تكن معتادًا عليه ، لكن لا تقلق - سأشرح كل شيء على طول الطريق!

ملاحظة: بينما نتحدث عن واجهات برمجة التطبيقات المضمنة ، فإن السبب وراء قدرتنا على استخدام هذه الفئة على الإطلاق هو بفضل الجسر الموجود في وقت تشغيل 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 NSBackingStoreBuffered defer false ، لذلك لا داعي للقلق بشأنهما. (يتطرق توثيق هذه الطريقة إلى مزيد من التفاصيل حول سبب ذلك.)
 window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);

هنا قمنا بتعيين NSWindow releasedWhenClosed عنها عند الإغلاق على false ، مما يعني: "Hey! لا تحذف هذه النافذة من الذاكرة لمجرد أن المستخدم يغلقها ". ثم نسمي 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 } في الأسفل؟ هذه طريقة لتحديد الكائنات والوظائف النصية التي يمكن أن تستورد رمز واجهة المستخدم هذا بالضبط (وإخفاء تلك التي لا نريد أن نقلق بشأنها) ، مما يعني أن لدينا الآن واجهة برمجة تطبيقات أكثر تنظيماً للتفاعل معها ، إظهار وتدمير واجهة المستخدم الخاصة بنا.

يوصى بالقراءة : إطلاق العنان للإمكانيات الكاملة للرموز في الرسم التخطيطي

دعونا نرى كيف يبدو هذا في الممارسة. مرة أخرى في index.js ، قم بإزالة الشفرة القديمة وإضافة ما يلي:

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

نحن نستخدم وظيفة خاصة يتيحها Sketch لنا تلقائيًا ، require ، لاستيراد رمز ui.js بنا وتعيين الوحدة النمطية التي تم إرجاعها إلى متغير UI . يتيح لنا هذا الوصول إلى واجهة برمجة تطبيقات مبسطة لتشغيل واجهة المستخدم الخاصة بنا. أصبحت الأمور أكثر تنظيمًا الآن ويسهل العثور عليها!

خاتمة

أحسنت - لقد قطعت شوطا بعيدا! 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!