defmodule AFK.Keycode.Modifier do
@moduledoc """
Represents a basic modifier keycode, like control, shift, etc.
All standard modifiers on a keyboard can be represented by `Modifier`
keycodes. The currently supported modifiers are `t:modifier/0`.
"""
@enforce_keys [:modifier]
defstruct [:modifier]
@type modifier ::
unquote(
AFK.Scancode.modifiers()
|> Enum.map(&elem(&1, 1))
|> Enum.reverse()
|> Enum.reduce(&{:|, [], [&1, &2]})
)
@type t :: %__MODULE__{
modifier: modifier
}
@doc """
Creates a basic modifier keycode.
## Examples
iex> new(:left_control)
%AFK.Keycode.Modifier{modifier: :left_control}
iex> new(:right_super)
%AFK.Keycode.Modifier{modifier: :right_super}
"""
@spec new(modifier) :: t
def new(modifier)
for {_value, modifier} <- AFK.Scancode.modifiers() do
def new(unquote(modifier)), do: struct!(__MODULE__, modifier: unquote(modifier))
end
defimpl AFK.Scancode.Protocol do
@spec scancode(keycode :: AFK.Keycode.Modifier.t()) :: AFK.Scancode.t()
def scancode(keycode)
for {value, modifier} <- AFK.Scancode.modifiers() do
def scancode(%AFK.Keycode.Modifier{modifier: unquote(modifier)}), do: unquote(value)
end
end
defimpl AFK.ApplyKeycode, for: AFK.Keycode.Modifier do
@spec apply_keycode(keycode :: AFK.Keycode.Modifier.t(), state :: AFK.State.t(), key :: atom) :: AFK.State.t()
def apply_keycode(keycode, state, key) do
modifier_used? =
Enum.any?(state.modifiers, fn
{_key, ^keycode} -> true
{_key, _keycode} -> false
end)
if modifier_used? do
state
else
modifiers = [{key, keycode} | state.modifiers]
%{state | modifiers: modifiers}
end
end
@spec unapply_keycode(keycode :: AFK.Keycode.Modifier.t(), state :: AFK.State.t(), key :: atom) :: AFK.State.t()
def unapply_keycode(keycode, state, key) do
modifiers =
Enum.filter(state.modifiers, fn
{^key, ^keycode} -> false
{_key, _keycode} -> true
end)
%{state | modifiers: modifiers}
end
end
end