option.tin 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. #nop vim: set filetype=tt:;
  2. /*
  3. 本文件属于 PaoTin++ 的一部分。
  4. PaoTin++ © 2020~2023 的所有版权均由担子炮(dzp <danzipao@gmail.com>) 享有并保留一切法律权利
  5. 你可以在遵照 GPLv3 协议的基础之上使用、修改及重新分发本程序。
  6. */
  7. #var lib_option[META] {
  8. {NAME} {全局选项管理}
  9. {DESC} {为全局选项提供了一个储存位置,并提供一些实用的 API。}
  10. {AUTHOR} {担子炮}
  11. };
  12. VAR {全局选项字典} gOptions {};
  13. load-lib storage;
  14. event.Define {option/changed} {无参} {$MODULE} {选项已更新。本事件只会唤醒和选项名称相对应的钩子};
  15. #func {lib_option.Init} {
  16. #var gOptions {@storage.Get{global-options}};
  17. #return {true};
  18. };
  19. ///=== {
  20. ///// 全局选项管理。
  21. /////
  22. ///// 全局选项本质上就是一些变量,这个模块主要是为了提供一些公共 API,和一个统
  23. ///// 一的管理方式,能够把散落在各处的选项集中管理。
  24. /////
  25. ///// 选项具有作用域:
  26. ///// - share: 跨角色共享,修改后所有本机角色都会受到影响。
  27. ///// - user: 按角色隔离,每个角色有自己的值,修改本角色不会影响其他角色。
  28. /////
  29. ///// 作用域在定义时指定,缺省为 user。可以在运行时通过 option.SetScope 调整。
  30. ///// 具体细节请参考 HELP option.SetScope
  31. /////
  32. // ## option.Define <名称> <数据类型> <中文含义> [<默认值>] [<作用域>]
  33. // 定义一个全局选项。选项被定义之后有一些公共 API 可供使用。
  34. // 选项必须经过定义之后才能使用。选项的名称按照大骆驼风格命名。
  35. // 作用域可选 share 或 user,缺省为 user。
  36. // 目前支持 Bool, Number, String, Slist, Time 等类型的选项。
  37. // };
  38. #alias {option.Define} {
  39. #local option {%1};
  40. #local type {%2};
  41. #local meaning {%3};
  42. #local default {%4};
  43. #local scope {@default{%5;user}};
  44. #if { "$option" == "" || "$type" == "" || "$meaning" == "" } {
  45. xtt.Usage option.Define;
  46. #return;
  47. };
  48. #if { "$scope" != "{share|user}" } {
  49. xtt.Usage option.Define 作用域只能是 share 或 user。;
  50. #return;
  51. };
  52. #if { "$gOptions[$option]" != "" } {
  53. #if { "$gOptions[$option][name]" != "$option"
  54. || "$gOptions[$option][type]" != "$type"
  55. || "$gOptions[$option][meaning]" != "$meaning"
  56. } {
  57. errLog 选项「$option」已存在,请检查代码。;
  58. };
  59. #if { "$gOptions[$option][scope]" == "" } {
  60. #nop 兼容旧数据,补齐缺省作用域。;
  61. #var gOptions[$option][scope] {$scope};
  62. };
  63. #elseif { "$gOptions[$option][scope]" != "$scope" } {
  64. errLog 选项「$option」的作用域与已有定义不一致,请检查代码。;
  65. };
  66. #return;
  67. };
  68. #if { ! @option.isValidValue{$type;{$default}} } {
  69. #return;
  70. };
  71. #var gOptions[$option] {
  72. {name} {$option}
  73. {type} {$type}
  74. {meaning} {$meaning}
  75. {value} {$default}
  76. {scope} {$scope}
  77. };
  78. option.persist;
  79. };
  80. ///=== {
  81. // ## option.List
  82. // 列出所有的选项。
  83. // };
  84. #alias {option.List} {
  85. option.ensure;
  86. #echo {};
  87. #echo { <128>%-25s %-10s %-8s %-30s %+22s} {选项名称} {选项类型} {作用域} {选项含义} {选项当前值};
  88. #draw Yellow scroll line 1 1 1 110;
  89. #local option {};
  90. #foreach {*gOptions[]} {option} {
  91. #local value {$gOptions[$option][value]};
  92. #if { "$gOptions[$option][type]" == "Time" } {
  93. #if { $value > 0 } {
  94. #format value {%t} {{%Y-%m-%d %H:%M:%S}{$value}};
  95. };
  96. };
  97. #echo { %-25s %-10s %-8s %-30s %+22s}
  98. {$gOptions[$option][name]}
  99. {$gOptions[$option][type]}
  100. {$gOptions[$option][scope]}
  101. {$gOptions[$option][meaning]}
  102. {{$value}};
  103. };
  104. #echo {};
  105. };
  106. ///=== {
  107. // #@ option.Get <选项名称>
  108. // 获取选项的当前值。
  109. // };
  110. #func {option.Get} {
  111. #local {option} {%1};
  112. #if { "$option" == "" } {
  113. xtt.Usage option.Get;
  114. #return {};
  115. };
  116. option.ensure;
  117. #if { "$gOptions[$option]" == "" } {
  118. errLog 不存在选项「$option」,请先定义后再使用。;
  119. #return {};
  120. };
  121. #return {$gOptions[$option][value]};
  122. };
  123. ///=== {
  124. // #@ option.Set <选项名称> <选项新值>
  125. // 设置选项的当前值,返回之前的旧值。
  126. // };
  127. #func {option.Set} {
  128. #local {option} {%1};
  129. #local {value} {%2};
  130. #if { "$option" == "" } {
  131. xtt.Usage option.Set;
  132. #return {};
  133. };
  134. option.ensure;
  135. #local old {$gOptions[$option]};
  136. #if { "$old" == "" } {
  137. errLog 不存在选项「$option」,请先定义后再使用。;
  138. #return {};
  139. };
  140. #if { ! @option.isValidValue{$gOptions[$option][type];{$value}} } {
  141. #return {};
  142. };
  143. #if { "$old[value]" !== "$value" } {
  144. #var {gOptions[$option][value]} {$value};
  145. option.persist;
  146. event.Emit {option/changed} {opt/$option/%*};
  147. };
  148. #return {$old[value]};
  149. };
  150. ///=== {
  151. // ## option.Set <选项名称> <选项值>
  152. // 设置选项的当前值。
  153. // };
  154. #alias {option.Set} {
  155. #local {option} {%1};
  156. #local {value} {%2};
  157. #local _ {@option.Set{$option;{$value}}};
  158. };
  159. ///=== {
  160. // ## option.Push <选项名称> <选项值>
  161. // 设置选项的当前值,并将旧值压入栈中。配合 option.Pop 可以实现选项值的回退。
  162. // };
  163. #alias {option.Push} {
  164. #local {option} {%1};
  165. #local {value} {%2};
  166. #if { "$option" == "" } {
  167. xtt.Usage option.Push;
  168. #return {};
  169. };
  170. option.ensure;
  171. #local old {$gOptions[$option]};
  172. #if { "$old" == "" } {
  173. errLog 不存在选项「$option」,请先定义后再使用。;
  174. #return;
  175. };
  176. #if { ! @option.isValidValue{$gOptions[$option][type];{$value}} } {
  177. #return;
  178. };
  179. #var {gOptions[$option][value]} {$value};
  180. #list gOptions[$option][stack] add {$old[value]};
  181. option.persist;
  182. event.Emit {option/changed} {opt/$option/%*};
  183. };
  184. ///=== {
  185. // ## option.Pop <选项名称> <选项值>
  186. // 从选项的历史值中按照后进先出的原则弹出一个值,使之成为当前值。配合 option.Push 可以实现选项值的回退。
  187. // };
  188. #alias {option.Pop} {
  189. #local {option} {%1};
  190. #if { "$option" == "" } {
  191. xtt.Usage option.Pop;
  192. #return {};
  193. };
  194. option.ensure;
  195. #local old {$gOptions[$option]};
  196. #if { "$old" == "" } {
  197. errLog 不存在选项「$option」,请先定义后再使用。;
  198. #return;
  199. };
  200. #if { &gOptions[$option][stack][] == 0 } {
  201. #return;
  202. };
  203. #var {gOptions[$option][value]} {$old[stack][-1]};
  204. #list gOptions[$option][stack] delete -1;
  205. option.persist;
  206. event.Emit {option/changed} {opt/$option/%*};
  207. };
  208. ///=== {
  209. // #@ option.IsEnable <选项名称>
  210. // 检查选项是否已被开启。
  211. // };
  212. #func {option.IsEnable} {
  213. #local option {%1};
  214. #if { "$option" == "" } {
  215. xtt.Usage option.IsEnable;
  216. #return {};
  217. };
  218. #if { "$gOptions[$option]" == "" } {
  219. errLog 不存在选项「$option」,请先定义后再使用。;
  220. #return {};
  221. };
  222. #if { "$gOptions[$option][type]" != "Bool" } {
  223. errLog 只有 Bool 型变量才支持这个 API。;
  224. #return {};
  225. };
  226. #local value {@option.Get{$option}};
  227. #return @isTrue{$value};
  228. };
  229. ///=== {
  230. // #@ option.IsDisable <选项名称>
  231. // 检查选项是否已被禁用。
  232. // };
  233. #func {option.IsDisable} {
  234. #local option {%1};
  235. #if { "$option" == "" } {
  236. xtt.Usage option.IsDisable;
  237. #return {};
  238. };
  239. #if { "$gOptions[$option]" == "" } {
  240. errLog 不存在选项「$option」,请先定义后再使用。;
  241. #return {};
  242. };
  243. #if { "$gOptions[$option][type]" != "Bool" } {
  244. errLog 只有 Bool 型变量才支持这个 API。;
  245. #return {};
  246. };
  247. #local value {@option.Get{$option}};
  248. #return @isFalse{$value};
  249. };
  250. ///=== {
  251. // #@ option.Toggle <选项名称>
  252. // 翻转选项的当前值。如果是启用则改为禁用;如果是禁用则改为启用。
  253. // 本函数将返回之前的值。
  254. // };
  255. #func {option.Toggle} {
  256. #local {option} {%1};
  257. #if { "$option" == "" } {
  258. xtt.Usage option.Toggle;
  259. #return {};
  260. };
  261. #if { "$gOptions[$option]" == "" } {
  262. errLog 不存在选项「$option」,请先定义后再使用。;
  263. #return {};
  264. };
  265. #if { "$gOptions[$option][type]" != "Bool" } {
  266. errLog 只有 Bool 型变量才支持这个 API。;
  267. #return {};
  268. };
  269. #local value {@option.Get{$option}};
  270. #if { "$value" == "true" } {
  271. option.Disable $option;
  272. infoLog <169>选项「<139>$gOptions[$option][meaning]($option)<169>」 <119>已禁用。<299>;
  273. };
  274. #elseif { "$value" == "false" } {
  275. option.Enable $option;
  276. infoLog <169>选项「<139>$gOptions[$option][meaning]($option)<169>」 <129>已启用。<299>;
  277. };
  278. #return {$value};
  279. };
  280. ///=== {
  281. // ## option.Toggle <选项名称>
  282. // 翻转选项的当前值。如果是启用则改为禁用;如果是禁用则改为启用。
  283. // };
  284. #alias {option.Toggle} {
  285. #local {option} {%1};
  286. #local _ {@option.Toggle{$option}};
  287. };
  288. ///=== {
  289. // #@ option.Scope <选项名称>
  290. // 获取选项的作用域,返回 share 或 user。
  291. // 参见 option.SetScope
  292. // };
  293. #func {option.Scope} {
  294. #local option {%1};
  295. #if { "$option" == "" } {
  296. #return {};
  297. };
  298. #if { "$gOptions[$option]" == "" } {
  299. #return {};
  300. };
  301. #return {$gOptions[$option][scope]};
  302. };
  303. ///=== {
  304. // ## option.SetScope <选项名称> <share|user>
  305. // 修改选项的作用域,并将选项数据从原来的存储迁移到新的存储。
  306. // NOTE: 本命令会移动选项的数据,操作前请确认该选项的当前值。
  307. // };
  308. #alias {option.SetScope} {
  309. #local option {%1};
  310. #local scope {%2};
  311. #if { "$option" == "" || "$scope" == "" } {
  312. xtt.Usage option.SetScope;
  313. #return;
  314. };
  315. #if { "$gOptions[$option]" == "" } {
  316. errLog 不存在选项「$option」,请先定义后再使用。;
  317. #return;
  318. };
  319. #if { "$scope" != "{share|user}" } {
  320. xtt.Usage option.SetScope 作用域只能是 share 或 user。;
  321. #return;
  322. };
  323. #local oldScope {$gOptions[$option][scope]};
  324. #if { "$oldScope" == "$scope" } {
  325. #return;
  326. };
  327. #var gOptions[$option][scope] {$scope};
  328. option.persist;
  329. infoLog 选项「$gOptions[$option][meaning]($option)」的作用域已从 $oldScope 调整为 $scope。;
  330. };
  331. ///=== {
  332. // ## option.Enable <选项名称>
  333. // 启用该选项。
  334. // };
  335. #alias {option.Enable} {
  336. #local {option} {%1};
  337. option.Set $option {true};
  338. };
  339. ///=== {
  340. // ## option.Disable <选项名称>
  341. // 禁用该选项。
  342. // };
  343. #alias {option.Disable} {
  344. #local {option} {%1};
  345. option.Set $option {false};
  346. };
  347. #func {@option.isValidValue} {
  348. #local type {%2};
  349. #local value {%3};
  350. #local types {Bool|String|Slist|Number|Time};
  351. #if { "$type" != "{$types}" } {
  352. errLog 不能识别的选项类型: 「$type」,目前仅支持选项类型:{$types};
  353. #return;
  354. };
  355. #if { "$type" == "Bool" && "$value" != "{true|false}" } {
  356. errLog Bool 型的选项值只能是 true 或者 false。;
  357. #return 0;
  358. };
  359. #else {
  360. #return 1;
  361. };
  362. };
  363. VAR {角色私有选项已加载} option-user-loaded {false};
  364. #nop 内部命令,确保角色私有选项已经从磁盘加载。;
  365. #alias {option.ensure} {
  366. #if { @isTrue{$option-user-loaded} || "$user[id]" == "" } {
  367. #return;
  368. };
  369. #local userOpts {@storage.GetUser{options}};
  370. #local opt {};
  371. #foreach {*userOpts[]} {opt} {
  372. #if { "$userOpts[$opt]" != "" } {
  373. #if { "$userOpts[$opt][scope]" == "" } {
  374. #nop 兼容旧数据,保留现有作用域。;
  375. #var userOpts[$opt][scope] {$gOptions[$opt][scope]};
  376. };
  377. #var gOptions[$opt] {$userOpts[$opt]};
  378. };
  379. };
  380. #var option-user-loaded {true};
  381. };
  382. #nop 内部命令,将选项字典持久化到磁盘。
  383. #nop global-options 键保存全部跨角色共享的选项;
  384. #nop options 键仅保存当前角色的私有选项,供不同角色隔离读取。;
  385. #alias {option.persist} {
  386. option.ensure;
  387. #if { "$user[id]" == "" } {
  388. #return;
  389. };
  390. #local globalOpts {};
  391. #local userOpts {};
  392. #local opt {};
  393. #foreach {*gOptions[]} {opt} {
  394. #if { "$gOptions[$opt][scope]" == "share" } {
  395. #var globalOpts[$opt] {$gOptions[$opt]};
  396. };
  397. #else {
  398. #var userOpts[$opt] {$gOptions[$opt]};
  399. };
  400. };
  401. storage.Set {global-options} {$globalOpts};
  402. storage.SetUser {options} {$userOpts};
  403. };