diff --git a/druid/extended/hotkey.lua b/druid/extended/hotkey.lua new file mode 100644 index 0000000..e95bdf3 --- /dev/null +++ b/druid/extended/hotkey.lua @@ -0,0 +1,148 @@ +-- Copyright (c) 2021 Maksim Tuprikov . This code is licensed under MIT license + +--- Druid hotkey component +-- @module Hotkey +-- @within BaseComponent +-- @alias druid.hotkey + +--- On change state callback(self, state) +-- @tfield DruidEvent on_change_state @{DruidEvent} + +--- Visual node +-- @tfield node node + +--- Button trigger node +-- @tfield[opt=node] node click_node + +--- Button component from click_node +-- @tfield Button button @{Button} + +--- + +local helper = require("druid.helper") +local component = require("druid.component") + +local Hotkey = component.create("hotkey") + + +--- Component init function +-- @tparam Hotkey self @{Hotkey} +-- @tparam string[]|string keys The keys to be pressed for trigger callback. Should contains one key and any modificator keys +-- @tparam function callback The callback function +function Hotkey.init(self, keys, callback, callback_argument) + self.druid = self:get_druid() + + self._hotkeys = {} + self._modificators = {} + self._callback = callback + + if keys then + self:add_hotkey(keys, callback_argument) + end +end + + +--- Component style params. +-- You can override this component styles params in druid styles table +-- or create your own style +-- @table style +-- @tfield string[] MODIFICATORS The list of action_id as hotkey modificators +function Hotkey.on_style_change(self, style) + self.style = {} + self.style.MODIFICATORS = style.MODIFICATORS or {} + + for index = 1, #style.MODIFICATORS do + self.style.MODIFICATORS[index] = hash(self.style.MODIFICATORS[index]) + end +end + + +--- Add hotkey for component callback +-- @tparam Hotkey self @{Hotkey} +-- @tparam string[]|hash[]|string|hash Keys that have to be pressed before key pressed to activate +function Hotkey.add_hotkey(self, keys, callback_argument) + keys = keys or {} + if type(keys) == "string" then + keys = { keys } + end + + local modificators = {} + local key = nil + + for index = 1, #keys do + local key_hash = hash(keys[index]) + if helper.contains(self.style.MODIFICATORS, key_hash) then + table.insert(modificators, key_hash) + else + if not key then + key = key_hash + else + error("The hotkey keys should contains only one key (except modificator keys)") + end + end + end + + table.insert(self._hotkeys, { + modificators = modificators, + key = key, + is_processing = false, + callback_argument = callback_argument, + }) + + -- Current hotkey status + for index = 1, #self.style.MODIFICATORS do + local modificator = hash(self.style.MODIFICATORS[index]) + self._modificators[modificator] = self._modificators[modificator] or false + end + + return self +end + + +function Hotkey.on_input(self, action_id, action) + if not action_id then + return + end + + if self._modificators[action_id] ~= nil then + if action.pressed then + self._modificators[action_id] = true + end + if action.released then + self._modificators[action_id] = false + end + end + + for index = 1, #self._hotkeys do + local hotkey = self._hotkeys[index] + if action_id == hotkey.key then + local is_modificator_ok = true + + -- Check only required modificators pressed + for i = 1, #self.style.MODIFICATORS do + local mod = self.style.MODIFICATORS[i] + if helper.contains(hotkey.modificators, mod) and self._modificators[mod] == false then + is_modificator_ok = false + end + if not helper.contains(hotkey.modificators, mod) and self._modificators[mod] == true then + is_modificator_ok = false + end + end + + if action.pressed and is_modificator_ok then + hotkey.is_processing = true + end + if action.released and is_modificator_ok and hotkey.is_processing then + hotkey.is_processing = false + if hotkey.callback_argument then + self._callback(self:get_context(), hotkey.callback_argument) + else + self._callback(self:get_context()) + end + end + end + end +end + + +return Hotkey