برنامه نویسی

ساخت تایمر Speedrun: فصل 3

رها کردن کشتی 🚢

خوب پس از چند پست وبلاگ موفق، ما بالاخره به اولین مشکل بزرگ خود رسیدیم، و این یک مانع نمایشی است. منظور من از آن چیست؟ خوب این به ملاحظات طراحی اولیه مربوط می شود.

وقتی برای اولین بار برنامه خود را طراحی کردم، مسیر PWA را طی کردم و فکر می کردم که مرورگر همه چیزهایی را که ما برای عملکرد صحیح برنامه خود نیاز داریم دارد.

چگونه به او بگوییم؟

اما من اشتباه می کردم. یکی از حیاتی‌ترین ویژگی‌های تایمر سرعت ما یعنی میانبرهای جهانی را نادیده گرفتم.

میانبرهای سراسری کلیدها یا ترکیبی از کلیدها هستند که می‌توانید روی صفحه‌کلید خود تایپ کنید تا نوعی عملکرد را در برنامه یا سیستم عامل خود فعال کنید (مثلاً کلید ویندوز راه‌انداز برنامه را باز می‌کند).

اگر با LiveSplit آشنایی دارید و مدت زمان مناسبی در سرعت دونده بوده اید، می دانید که کلیدهای میانبر سراسری یک نجات دهنده وقتی نوبت به ردیابی تقسیمات شما می رسد. این عملی نیست که مطمئن شوید زمانی که یک بازی را با سرعت اجرا می کنید، تایمر شما در پیش زمینه باقی می ماند. خیلی اوقات ممکن است به طور تصادفی برنامه دیگری را باز بگذارید و زمان تقسیم یا کل اجرای خود را کاملاً بهم بریزد!

پس میدونی چیه نمی تواند انجام میانبرهای جهانی؟ مرورگر.

مهاجرت از PWA به الکترون

اوه پسر، در این مورد چه کنیم؟ خوب خوشبختانه، ما از گزینه ها خارج نیستیم. در واقع، من در ابتدا به نوعی این را توضیح دادم.

یکی از دلایلی که من تصمیم گرفتم برای این پروژه در Vue/JavaScript بنویسم، این است که در صورت نیاز، می‌توانم به راحتی به یک برنامه Electron مهاجرت کنم. Electron.js راهی برای ساخت برنامه های دسکتاپ چند پلتفرمی در جاوا اسکریپت به ما می دهد. بنابراین از طریق این مهاجرت، ما می توانیم از میانبرهای جهانی استفاده کنیم و برنامه خود را بر روی تمام پلتفرم های اصلی دسکتاپ اجرا کنیم!

تغییرات کد

من هرگز به تغییر کد نمی پردازم، زیرا این یک پست دیوانه کننده طولانی خواهد بود. علاوه بر این، کد ما تا حد زیادی یکسان باقی می ماند. چی اراده تغییر ساختار پروژه ما و عملکرد میانبر جهانی اضافه شده است.

ساختار پروژه ما به 3 بخش تقسیم می شود:

  • کد node.js که بین برنامه ما و سیستم عامل ارتباط برقرار می کند.
  • کد رندر، که در اصل فقط ظاهر ماست.
  • کد preloader که به عنوان پلی بین node.js و frontend عمل می کند.

کد Node.js

کد node.js ما عمدتاً هنگام داربست پروژه Electron Forge به صورت پیش فرض به ما داده می شود. بسیاری از نظرات نیز از آن ایجاد می شود. تنها کدی که تغییر می دهیم این است که می خواهیم پس از آماده شدن برنامه، میانبرهای جهانی را ثبت کنیم. ما این کار را با node.js انجام می دهیم زیرا باید به ورودی صفحه کلید در سطح سیستم عامل گوش دهیم، نه فقط در خود برنامه. هنگامی که عملکرد برگشت به تماس فعال شد، دستورات را به آن ارسال می کنیم mainWindow برای تعامل با تایمر ما

توجه به این نکته مهم است که پس از اتصال میانبر صفحه کلید (مانند عدد “1” که در اینجا نشان داده شده است)، نمی توانید با آن کلید تایپ کنید یا در هیچ برنامه دیگری از آن استفاده کنید. این محدودیت یک انتخاب طراحی توسط تیم Electron برای جلوگیری از توسعه یک کی لاگر بود. ممکن است نوعی راه حل وجود داشته باشد، اما من در اینجا وارد آن نمی شوم. اگر راه حل دیگری را می شناسید، نظر خود را اعلام کنید!

// main.js
const { app, BrowserWindow, globalShortcut } = require('electron');
const path = require('path');

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
  app.quit();
}

let mainWindow;
const createWindow = () => {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    },
  });

  // and load the index.html of the app.
  if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
    mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
  } else {
    mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
  }

  // Open the DevTools.
  mainWindow.webContents.openDevTools();
};

// This method will be used to setup our global shortcuts code.
// https://electronjs.org/docs/latest/api/global-shortcut
app.whenReady().then(() => {
  globalShortcut.register('1', () => {
    mainWindow.webContents.send("global-timer", 0);
  })
  globalShortcut.register('2', () => {
    mainWindow.webContents.send("global-timer", 1);
  })
  globalShortcut.register('3', () => {
    mainWindow.webContents.send("global-timer", 2);
  })
}).then(createWindow);

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On OS X it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

Preloader

همانطور که قبلا ذکر شد، این کد به عنوان یک پل بین کد node.js (ارتباطات سیستم عامل) و کد رندر (frontend) عمل می کند. در حال حاضر، تنها کاری که باید انجام دهیم این است که آن را افشا کنیم ipcRenderer API اجازه می دهد تا رندر با گره ارتباط برقرار کند.

// preloader.js
// See the Electron documentation for details on how to use preload scripts:
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electronAPI', {
    listenForTimerCommands: (listener) => ipcRenderer.on('global-timer', listener)
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

رندر

در نهایت، ما رندر خود را داریم. این کد به سادگی فایل ورودی ما (main.js قدیمی) از PWA قبلی ما است.

// renderer.js
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";

createApp(App).mount("#app");
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بیایید ادامه دهیم و کد را در قسمت جلویی اضافه کنیم تا به ورودی میانبر جهانی واکنش نشان دهیم.

// Stopwatch.vue
<script setup>
import { onMounted, onUnmounted } from 'vue';
import { useStopwatch } from '../composables/stopwatch';
import { WorkerCommands } from '../helpers/timer-worker-helper';

const {
  timerTxt,
  onTimerStart,
  onTimerStop,
  onTimerReset,
  onTimerInit,
  onTimerTeardown
} = useStopwatch();

onMounted(() => {
  onTimerInit();
  window.electronAPI.listenForTimerCommands((_, data) => {
    switch(data) {
      case WorkerCommands.START:
        onTimerStart();
        break;
      case WorkerCommands.STOP:
        onTimerStop();
        break;
      case WorkerCommands.RESET:
        onTimerReset();
        break;
      }
  });
});

onUnmounted(() => {
  onTimerTeardown();
});

</script>

<template>
  <div>
    <p>{{ timerTxt }}</p>
    <button @mousedown="onTimerStart">Start</button>
    <button @mousedown="onTimerStop">Stop</button>
    <button @mousedown="onTimerReset">Reset</button>
  </div>
</template>

<style scoped>
button {
  margin: 0 5px;
}
</style>
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه

شما آن را دارید! اکنون با موفقیت از PWA به Electron مهاجرت کرده ایم. من در واقع بسیار هیجان زده هستم که ببینم توسعه دسکتاپ برای ما چه خواهد داشت. من قبلاً خیلی چیزها یاد گرفته ام!

اگر تغییر کد دیگری وجود دارد که می‌خواهید مشاهده کنید یا می‌خواهید من آن را مرور کنم، در مورد پست نظر خود را با سوالات خود در میان بگذارید. همچنین می توانید در مخزن GitHub دنبال کنید.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا