Реалізація гарячих клавіш (Keyboard Shortcuts) у десктоп-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація гарячих клавіш (Keyboard Shortcuts) у десктоп-застосунку
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація горячих клавіш (Keyboard Shortcuts) у десктоп-приложенні

Горячі клавіші в десктоп-приложенні працюють на двох рівнях: локальний (лише коли приложение в фокусі) та глобальний (працює поверх інших вікон). Electron та Tauri реалізують обидва рівні по-різному. Хороша система включає registry, parser, matcher, scope та persistence.

Архітектура системи хоткеїв

// src/shortcuts/types.ts
export interface KeyCombo {
  key: string
  ctrl?: boolean
  shift?: boolean
  alt?: boolean
  meta?: boolean
}

export interface ShortcutDefinition {
  id: string
  combo: KeyCombo | KeyCombo[]
  scope: string
  label: string
  action: () => void
  allowInInput?: boolean
}

Парсер та нормалізація

// src/shortcuts/parser.ts
const KEY_ALIASES: Record<string, string> = {
  'esc': 'Escape',
  'del': 'Delete',
  'return': 'Enter',
  'space': ' ',
}

export function parseCombo(input: string): KeyCombo {
  const parts = input.toLowerCase().split('+').map(p => p.trim())
  const combo: KeyCombo = { key: '' }

  for (const part of parts) {
    switch (part) {
      case 'ctrl':  combo.ctrl = true; break
      case 'shift': combo.shift = true; break
      case 'alt':   combo.alt = true; break
      case 'meta': combo.meta = true; break
      default: combo.key = part.toUpperCase()
    }
  }
  return combo
}

export function matchesEvent(combo: KeyCombo, event: KeyboardEvent): boolean {
  return (
    event.key === combo.key &&
    !!event.ctrlKey === !!combo.ctrl &&
    !!event.shiftKey === !!combo.shift &&
    !!event.altKey === !!combo.alt &&
    !!event.metaKey === !!combo.meta
  )
}

Registry

// src/shortcuts/registry.ts
export class ShortcutRegistry {
  private shortcuts = new Map<string, ShortcutDefinition>()

  register(def: ShortcutDefinition): ShortcutConflict | null {
    const conflict = this.findConflict(def)
    if (conflict) return conflict
    this.shortcuts.set(def.id, def)
    return null
  }

  unregister(id: string) {
    this.shortcuts.delete(id)
  }

  updateCombo(id: string, newCombo: KeyCombo | string) {
    const def = this.shortcuts.get(id)
    if (!def) return
    const parsed = typeof newCombo === 'string' ? parseCombo(newCombo) : newCombo
    this.shortcuts.set(id, { ...def, combo: parsed })
  }

  handleKeyEvent(event: KeyboardEvent, currentScope: string): boolean {
    const target = event.target as HTMLElement
    const isInputFocused =
      target.tagName === 'INPUT' ||
      target.tagName === 'TEXTAREA' ||
      target.isContentEditable

    for (const [, def] of this.shortcuts) {
      if (def.scope !== currentScope && def.scope !== 'global') continue
      if (isInputFocused && !def.allowInInput) continue

      const combos = Array.isArray(def.combo) ? def.combo : [def.combo]
      if (combos.some(c => matchesEvent(c, event))) {
        event.preventDefault()
        def.action()
        return true
      }
    }
    return false
  }
}

Глобальні хоткеї в Electron

// main/global-shortcuts.ts
import { globalShortcut } from 'electron'

export function registerGlobalShortcuts(win: BrowserWindow) {
  globalShortcut.register('CommandOrControl+Shift+Space', () => {
    if (win.isVisible()) {
      win.hide()
    } else {
      win.show()
      win.focus()
    }
  })

  globalShortcut.register('CommandOrControl+Shift+N', () => {
    win.webContents.send('shortcut:quick-note')
  })

  return () => globalShortcut.unregisterAll()
}

Хук для Renderer

// src/hooks/useShortcuts.ts
import { useEffect, useRef } from 'react'
import { ShortcutRegistry } from '../shortcuts/registry'
import { ShortcutDefinition } from '../shortcuts/types'

const registry = new ShortcutRegistry()

export function useShortcuts(
  shortcuts: Omit<ShortcutDefinition, 'id'>[],
  scope: string
) {
  useEffect(() => {
    const ids: string[] = []

    shortcuts.forEach((s, i) => {
      const id = `${scope}-${i}-${Date.now()}`
      const conflict = registry.register({ ...s, id, scope })
      if (conflict) {
        console.warn(`Shortcut conflict: "${s.label}" конфліктує з "${conflict.existing.label}"`)
        return
      }
      ids.push(id)
    })

    const handler = (e: KeyboardEvent) => {
      registry.handleKeyEvent(e, scope)
    }

    document.addEventListener('keydown', handler)

    return () => {
      document.removeEventListener('keydown', handler)
      ids.forEach(id => registry.unregister(id))
    }
  }, [])
}

Кастомізація користувачем

// main/shortcut-store.ts
import Store from 'electron-store'

const store = new Store()

export function getCustomBinding(id: string) {
  return store.get(`customBindings.${id}`, null)
}

export function saveCustomBinding(id: string, combo: KeyCombo) {
  store.set(`customBindings.${id}`, combo)
}

Базова реалізація: 3–4 години. Повна система з registry, scope-ами, chord-послідовностями, глобальними хоткеями, UI переназначення, персистентністю: 3–4 робочих дні.