1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
#![cfg(any(
  target_os = "windows",
  target_os = "macos",
  target_os = "linux",
  target_os = "dragonfly",
  target_os = "freebsd",
  target_os = "netbsd",
  target_os = "openbsd"
))]

//! **UNSTABLE** -- The `GlobalShortcut` struct and associated types.
//!
//! ```rust,ignore
//! let mut hotkey_manager = ShortcutManager::new(&event_loop);
//! let accelerator = Accelerator::new(SysMods::Shift, KeyCode::ArrowUp);
//! let global_shortcut = hotkey_manager.register(accelerator)?;
//! ```

use crate::{
  accelerator::Accelerator,
  event_loop::EventLoopWindowTarget,
  platform_impl::{
    GlobalShortcut as GlobalShortcutPlatform, ShortcutManager as ShortcutManagerPlatform,
  },
};
use std::{error, fmt};

/// Describes a global keyboard shortcut.
#[derive(Debug, Clone)]
pub struct GlobalShortcut(pub(crate) GlobalShortcutPlatform);

/// Object that allows you to manage a `GlobalShortcut`.
#[derive(Debug)]
pub struct ShortcutManager {
  registered_hotkeys: Vec<Accelerator>,
  p: ShortcutManagerPlatform,
}

impl ShortcutManager {
  /// Creates a new shortcut manager instance connected to the event loop.
  pub fn new<T: 'static>(event_loop: &EventLoopWindowTarget<T>) -> ShortcutManager {
    ShortcutManager {
      p: ShortcutManagerPlatform::new(event_loop),
      registered_hotkeys: Vec::new(),
    }
  }

  /// Whether the application has registered this `Accelerator`.
  pub fn is_registered(&self, accelerator: &Accelerator) -> bool {
    self.registered_hotkeys.contains(&Box::new(accelerator))
  }

  /// Register a global shortcut of `Accelerator` who trigger `GlobalShortcutEvent` in the event loop.
  pub fn register(
    &mut self,
    accelerator: Accelerator,
  ) -> Result<GlobalShortcut, ShortcutManagerError> {
    if self.is_registered(&accelerator) {
      return Err(ShortcutManagerError::AcceleratorAlreadyRegistered(
        accelerator,
      ));
    }
    self.registered_hotkeys.push(accelerator.clone());
    self.p.register(accelerator)
  }

  /// Unregister all `Accelerator` registered by the manager instance.
  pub fn unregister_all(&mut self) -> Result<(), ShortcutManagerError> {
    self.registered_hotkeys = Vec::new();
    self.p.unregister_all()
  }

  /// Unregister the provided `Accelerator`.
  pub fn unregister(
    &mut self,
    global_shortcut: GlobalShortcut,
  ) -> Result<(), ShortcutManagerError> {
    self
      .registered_hotkeys
      .retain(|hotkey| hotkey.to_owned().id() != global_shortcut.0.id());
    self.p.unregister(global_shortcut)
  }
}

/// An error whose cause the `ShortcutManager` to fail.
#[non_exhaustive]
#[derive(Debug)]
pub enum ShortcutManagerError {
  AcceleratorAlreadyRegistered(Accelerator),
  AcceleratorNotRegistered(Accelerator),
  InvalidAccelerator(String),
}

impl error::Error for ShortcutManagerError {}
impl fmt::Display for ShortcutManagerError {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
    match self {
      ShortcutManagerError::AcceleratorAlreadyRegistered(e) => {
        f.pad(&format!("hotkey already registered: {:?}", e))
      }
      ShortcutManagerError::AcceleratorNotRegistered(e) => {
        f.pad(&format!("hotkey not registered: {:?}", e))
      }
      ShortcutManagerError::InvalidAccelerator(e) => e.fmt(f),
    }
  }
}