Coverage for src/pyselector/key_manager.py: 93%

60 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-29 12:33 -0300

1# keybinds.py 

2from __future__ import annotations 

3 

4from dataclasses import dataclass 

5from typing import Any 

6from typing import Callable 

7from typing import Optional 

8 

9 

10class KeybindError(Exception): 

11 pass 

12 

13 

14@dataclass(kw_only=True) 

15class Keybind: 

16 """ 

17 Represents a keybind, which associates a keyboard key or 

18 combination of keys with a callback function. 

19 

20 Attributes: 

21 id (int): The unique identifier of the keybind. 

22 bind (str): The key or key combination that triggers the keybind. 

23 code (int): The unique code of the keybind. 

24 description (str): A brief description of the keybind. 

25 action (Optional[str]): An optional action associated with the keybind. Defaults to an empty string. 

26 hidden (bool): Whether the keybind is hidden from the user interface. Defaults to True. 

27 callback (Optional[Callable[..., Any]]): The function to call when the keybind is triggered. Defaults to None. 

28 

29 Methods: 

30 toggle_hidden(): Toggles the visibility of the keybind in the user interface. 

31 """ 

32 

33 id: int 

34 bind: str 

35 code: int 

36 description: str 

37 action: Optional[str] = "" 

38 hidden: bool = True 

39 callback: Optional[Callable[..., Any]] = None 

40 

41 def toggle_hidden(self) -> None: 

42 self.hidden = not self.hidden 

43 

44 

45class KeyManager: 

46 """ 

47 A class for managing keybinds, which are associations between key combinations 

48 and callback functions. 

49 

50 Attributes: 

51 keys (dict[str, Keybind]): A dictionary mapping keybinds to their corresponding `Keybind` objects. 

52 key_count (int): A counter for assigning unique IDs to newly added keybinds. 

53 code_count (int): A counter for assigning unique codes to newly added keybinds. 

54 temp_hidden (list[Keybind]): A list of temporarily hidden keybinds. 

55 """ 

56 

57 def __init__(self) -> None: 

58 self.keys: dict[str, Keybind] = {} 

59 self.key_count = 1 

60 self.code_count = 1 

61 self.temp_hidden: list[Keybind] = [] 

62 

63 def add( 

64 self, 

65 key: str, 

66 description: str, 

67 callback: Callable[..., Any], 

68 hidden: bool = False, 

69 exist_ok: bool = False, 

70 ) -> Keybind: 

71 """ 

72 Registers a new keybind with the specified bind and description, 

73 and associates it with the specified callback function. 

74 

75 Args: 

76 key (str): The bind of the keybind. 

77 description (str): The description of the keybind. 

78 callback (Callable[..., Any]): The function to call when the keybind is triggered. 

79 hidden (bool): Whether the keybind should be hidden from the user interface. Defaults to False. 

80 exist_ok (bool): Whether to overwrite an existing keybind with the same bind. Defaults to False. 

81 """ 

82 return self.register( 

83 Keybind( 

84 id=self.key_count, 

85 bind=key, 

86 code=self.code_count, 

87 description=description, 

88 hidden=hidden, 

89 callback=callback, 

90 ), 

91 exist_ok=exist_ok, 

92 ) 

93 

94 def unregister(self, bind: str) -> None: 

95 """Removes the keybind with the specified bind.""" 

96 if not self.keys.get(bind): 

97 raise KeybindError(f"{bind=} not found") 

98 self.keys.pop(bind) 

99 

100 def register(self, key: Keybind, exist_ok: bool = False) -> Keybind: 

101 """ 

102 Args: 

103 key (Keybind): The keybind to register. 

104 exist_ok (bool): Whether to overwrite an existing keybind with the same bind. Defaults to False. 

105 

106 Returns: 

107 Keybind: The registered keybind. 

108 

109 Raises: 

110 KeybindError: If `exist_ok` is False and a keybind with the same bind is already registered. 

111 """ 

112 if exist_ok and self.keys.get(key.bind): 

113 self.unregister(key.bind) 

114 

115 if self.keys.get(key.bind): 

116 raise KeybindError(f"{key.bind=} already registered") 

117 

118 self.key_count += 1 

119 self.code_count += 1 

120 self.keys[key.bind] = key 

121 return key 

122 

123 def patch(self, bind: str) -> Keybind: 

124 # FIX: delete me 

125 key = self.keys.get(bind) 

126 if not key: 

127 raise KeybindError(f"{bind=} not found") 

128 return key 

129 

130 @property 

131 def all_registered(self) -> list[Keybind]: 

132 return list(self.keys.values()) 

133 

134 def toggle_all(self) -> None: 

135 """Toggles the "hidden" property of all non-hidden keybinds.""" 

136 for key in self.all_registered: 

137 if not key.hidden: 

138 key.hidden = True 

139 

140 def toggle_hidden(self, restore: bool = False) -> None: 

141 """ 

142 Toggles the "hidden" property of all non-hidden keybinds, and 

143 temporarily stores the original "hidden" state of each keybind. 

144 If `restore` is True, restores the original "hidden" state of each keybind. 

145 """ 

146 for key in self.all_registered: 

147 if not key.hidden: 

148 key.toggle_hidden() 

149 self.temp_hidden.append(key) 

150 if restore: 

151 for key in self.temp_hidden: 

152 key.toggle_hidden() 

153 self.temp_hidden = []