event.tin 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #nop vim: set filetype=tt:;
  2. /*
  3. 本文件属于 PaoTin++ 的一部分
  4. ===========
  5. PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 享有并保留一切法律权利
  6. 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。
  7. ===========
  8. */
  9. ///=== {
  10. ///// event 模块实现了一个事件驱动编程框架,
  11. ///// 提供基本的事件驱动编程 API,允许用户定义、发射、订阅事件。
  12. ///// };
  13. #var lib_event[META] {
  14. {NAME} {事件驱动编程框架}
  15. {DESC} {提供基本的事件驱动编程 API,允许用户定义、发射、订阅事件}
  16. {AUTHOR} {担子炮}
  17. {NOTE} {本文件属于 PaoTin++ 的一部分}
  18. };
  19. #func {lib_event.Init} {
  20. #class data/lib/event open;
  21. #var gEventHandlers {};
  22. #var gValidEvent {};
  23. #class data/lib/event close;
  24. #return true;
  25. };
  26. #func {__xtt_event_name_is_valid__} {
  27. #local name {%1};
  28. #if { "$name" == "{[_a-zA-Z]([./_a-zA-Z0-9-]*[a-zA-Z0-9])?}" } {
  29. #return {true};
  30. };
  31. #return {false};
  32. };
  33. ///=== {
  34. // ## event.Define <名称> <类型> <模块> <说明>
  35. // 定义事件。事件在使用前必须先定义。事件经过定义后,可以用 event.List 查看。
  36. // 参数列表:
  37. // - 名称:标识事件的唯一名称,只能以拉丁字母或下划线开头,后面跟若干个
  38. // 字母、数字、下划线(_)、斜线(/)、小数点(.) 组成。其中三个
  39. // 标点符号不能出现在末尾,只能出现在中间。
  40. // - 类型:枚举值,{有参} 或者 {无参} 二选一。
  41. // 如果事件被定义为有参,则允许发射事件时携带参数,事件驱动会将
  42. // 参数传递给事件处理句柄。
  43. // - 模块:标识事件所属模块,一般来说事件发射方为事件所属模块。
  44. // 这里要用标准的 PaoTin++ 模块描述符。
  45. // - 说明:事件的简短说明。会出现在类似于 event.List 的用户交互界面。
  46. // };
  47. #alias {event.Define} {
  48. #local name {%1};
  49. #local type {%2};
  50. #local module {%3};
  51. #local desc {%4};
  52. #if { "@__xtt_event_name_is_valid__{{$name}}" != "true" } {
  53. xtt.Usage event.Define 事件名称不是合法的标识符名称;
  54. #return;
  55. };
  56. #if { "$type" == "" } {
  57. #local type {无参};
  58. };
  59. #if { "$type" != "{有参|无参}" } {
  60. xtt.Usage event.Define 事件类型参数值不正确;
  61. #return;
  62. };
  63. #var {gValidEvent[$name]} {
  64. {type}{$type}
  65. {module}{$module}
  66. {desc}{$desc}
  67. };
  68. };
  69. ///=== {
  70. // ## event.List
  71. // 列出所有已定义的事件,以及目前已注册在这些事件上面的钩子。
  72. // };
  73. #alias {event.List} {
  74. #local event {};
  75. #if { &gValidEvent[] <= 0 } {
  76. infoLog 尚未定义任何事件。;
  77. #return;
  78. };
  79. #echo {%h} { 已经定义的事件列表 };
  80. #echo {%-20s %-5s %-30s %s} {事件/已注册的钩子} {类型} {模块} {说明/代码};
  81. #echo {%-20s %-5s %-30s %s} {@str.Repeat{20;-}} {----} {@str.Repeat{30;-}} {------------};
  82. #foreach {*gValidEvent[]} {event} {
  83. #local type {有参};
  84. #if { "$gValidEvent[$event][type]" == "{无参|}" } {
  85. #local type {无参};
  86. };
  87. #echo {%-20s %-5s %-30s %s}{$event} {$type}
  88. {@genModuleLink{$gValidEvent[$event][module];MOD}}
  89. {$gValidEvent[$event][desc]};
  90. #local hook {};
  91. #local count {0};
  92. #foreach {*gEventHandlers[$event][]} {hook} {
  93. #local len {1};
  94. #format len {%L} {$hook};
  95. #math len {16 - $len};
  96. #math count {$count + 1};
  97. #local lead {├};
  98. #if { $count == &gEventHandlers[$event][] } {
  99. #local lead {╰};
  100. };
  101. #echo { $lead@str.Repeat{$len;─} %s %-30s %s}{$hook}
  102. {@genModuleLink{$gEventHandlers[$event][$hook][module];MOD}}
  103. {$gEventHandlers[$event][$hook][code]};
  104. };
  105. };
  106. #echo {%h};
  107. };
  108. ///=== {
  109. // ## event.Emit <事件名称> [<回调钩子通配符>] [<事件参数>]
  110. // 发射事件。这将导致与回调钩子通配符相匹配的回调钩子被立即执行。
  111. // 默认会触发所有注册在本事件下的事件回调钩子。
  112. // 你可以参考 event.Handle 理解什么是事件回调钩子。
  113. // };
  114. #alias {event.Emit} {
  115. #local name {%1};
  116. #local pHook {%2};
  117. #local args {%3};
  118. #if { "@__xtt_event_name_is_valid__{{$name}}" != "true" } {
  119. xtt.Usage event.Emit 事件名称不是合法的标识符名称;
  120. #return;
  121. };
  122. #if { "$gValidEvent[$name]" == "" } {
  123. xtt.Usage event.Emit {未定义的事件名称: $name};
  124. #return;
  125. };
  126. #if { &gEventHandlers[$name][] <= 0 } {
  127. dbgLog event => 事件「$name」已产生,但因为没有注册接受者所以无法投递。;
  128. #return;
  129. };
  130. #local delivered {false};
  131. #local hook {};
  132. #foreach {*gEventHandlers[$name][]} {hook} {
  133. #local options {$gEventHandlers[$name][$hook][options]};
  134. #local code {$gEventHandlers[$name][$hook][code]};
  135. #nop 如果发射事件时指定了 pHook,则只唤醒指定的 hook,注意这里的 pHook 支持通配符;
  136. #if { "$pHook" != "" && "$hook" != "$pHook" } {
  137. #continue;
  138. };
  139. #local delivered {true};
  140. dbgLog event => 事件「$name」即将投递给「$gEventHandlers[$name][$hook][module]」模块的「$hook」。;
  141. #if { "$options[justOnce]" == "true" } {
  142. #unvar {gEventHandlers[$name][$hook]};
  143. };
  144. #if { "$args" == "" || "$gValidEvent[$name][type]" == "无参" } {
  145. $code;
  146. };
  147. #else {
  148. $code {$args};
  149. };
  150. };
  151. #if { @isFalse{$delivered} } {
  152. dbgLog event => 事件「$name」已产生,但因为没有匹配的接受者所以无法投递。;
  153. };
  154. };
  155. ///=== {
  156. // ## event.DelayEmit <事件名称> [<回调钩子通配符>] [<事件参数>]
  157. // 延迟发射事件。类似于 event.Emit,但是会在当前触发执行完毕之后再发射事件。
  158. // };
  159. #alias {event.DelayEmit} {
  160. #if { "@__xtt_event_name_is_valid__{%1}" != "true" } {
  161. xtt.Usage event.DelayEmit;
  162. #return;
  163. };
  164. #delay 0 {event.Emit %0};
  165. };
  166. ///=== {
  167. // ## event.Handle <事件名称> <回调钩子> <所属模块> <回调代码>
  168. // 注册事件回调钩子。参数说明如下:
  169. // - 事件名称: 本钩子要关联的事件的名称,需要事先用 event.Define 声明。
  170. // - 回调钩子: 本次注册的钩子,可以在随后用来取消本钩子,或者当事件发射时,
  171. // 发射方可以用正则表达式指定要触发哪些钩子。
  172. // - 所属模块: 注册钩子所在的代码模块。必须是一个严格的 PaoTin++ 模块描述符。
  173. // - 回调代码: 用来指明钩子被回调时要执行的代码。
  174. // };
  175. #alias {event.Handle} {
  176. #local name {%1};
  177. #local hook {%2};
  178. #local module {%3};
  179. #local code {%4};
  180. #if { "$name" == "" || "$hook" == "" || "$module" == "" || {$code} == {} } {
  181. xtt.Usage event.Handle;
  182. #return;
  183. };
  184. #if { "@__xtt_event_name_is_valid__{{$name}}" != "true" } {
  185. xtt.Usage event.Handle 事件名称不是合法的标识符名称;
  186. #return;
  187. };
  188. #var {gEventHandlers[$name][$hook]} {
  189. {module}{$module}
  190. {code}{$code}
  191. };
  192. };
  193. ///=== {
  194. // ## event.HandleOnce <事件名称> <回调钩子> <所属模块> <回调代码>
  195. // 注册事件回调钩子,但是本钩子只会被执行一次,然后会自动注销。
  196. // 参数说明如下:
  197. // - 事件名称: 本钩子要关联的事件的名称,需要事先用 event.Define 声明。
  198. // - 回调钩子: 本次注册的钩子,可以在随后用来取消本钩子,或者当事件发射时,
  199. // 发射方可以用正则表达式指定要触发哪些钩子。
  200. // - 所属模块: 注册钩子所在的代码模块。必须是一个严格的 PaoTin++ 模块描述符。
  201. // - 回调代码: 用来指明钩子被回调时要执行的代码。
  202. // };
  203. #alias {event.HandleOnce} {
  204. #local name {%1};
  205. #local hook {%2};
  206. #local module {%3};
  207. #local code {%4};
  208. #if { "$name" == "" || "$hook" == "" || "$module" == "" || {$code} == {} } {
  209. xtt.Usage event.HandleOnce;
  210. #return;
  211. };
  212. #if { "@__xtt_event_name_is_valid__{{$name}}" != "true" } {
  213. xtt.Usage event.HandleOnce 事件名称不是合法的标识符名称;
  214. #return;
  215. };
  216. #var {gEventHandlers[$name][$hook]} {
  217. {options}{{justOnce}{true}}
  218. {module}{$module}
  219. {code}{$code}
  220. };
  221. };
  222. ///=== {
  223. // ## event.UnHandle <事件名称> <事件回调钩子名称>
  224. // 注销已注册的事件回调钩子。
  225. // };
  226. #alias {event.UnHandle} {
  227. #local name {%1};
  228. #local hook {%2};
  229. #if { "$name" == "" || "$hook" == "" } {
  230. xtt.Usage event.UnHandle;
  231. #return;
  232. };
  233. #if { "@__xtt_event_name_is_valid__{{$name}}" != "true" } {
  234. xtt.Usage event.UnHandle 事件名称不是合法的标识符名称;
  235. #return;
  236. };
  237. #unvar {gEventHandlers[$name][$hook]};
  238. };