AdapterSafeArea.ts 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. import { Widget, _decorator } from "cc";
  2. import { EDITOR } from "cc/env";
  3. import { Adapter } from "./Adapter";
  4. const { ccclass, property, executeInEditMode, menu } = _decorator;
  5. enum Flags {
  6. None = 0,
  7. TOP = 1 << 0,
  8. BOTTOM = 1 << 1,
  9. LEFT = 1 << 2,
  10. RIGHT = 1 << 3,
  11. }
  12. /**
  13. * @classdesc 安全区域适配Widget , App.isFullScreenAdaption = true 时有效
  14. * @description
  15. *
  16. * 用法:
  17. *
  18. * 1. 将本组件挂载在节点上即可(注意:该节点上必须挂在 Widget 组件)
  19. *
  20. * 适配原理:
  21. *
  22. * 1. 根据安全区域范围,修改widget组件属性
  23. * 自动添加刘海宽度,以避免显示到安全区域之外
  24. */
  25. @ccclass("AdapterSafeArea")
  26. @executeInEditMode
  27. @menu("Quick适配组件/AdapterSafeArea")
  28. export default class AdapterSafeArea extends Adapter {
  29. @property
  30. protected _flags: number = Flags.None;
  31. protected setFlag(flag: Flags, value: boolean) {
  32. const current = (this._flags & flag) > 0;
  33. if (value == current) {
  34. return;
  35. }
  36. if (value) {
  37. this._flags |= flag;
  38. } else {
  39. this._flags &= ~flag;
  40. }
  41. this._isDirty = true;
  42. }
  43. @property({ tooltip: EDITOR ? "是否对齐上边" : "" })
  44. get isAlignTop() {
  45. return (this._flags & Flags.TOP) > 0;
  46. }
  47. set isAlignTop(v) {
  48. this.setFlag(Flags.TOP, v);
  49. }
  50. @property({ tooltip: EDITOR ? "是否对齐下边" : "" })
  51. get isAlignBottom() {
  52. return (this._flags & Flags.BOTTOM) > 0;
  53. }
  54. set isAlignBottom(v) {
  55. this.setFlag(Flags.BOTTOM, v);
  56. }
  57. @property({ tooltip: EDITOR ? "是否对齐左边" : "" })
  58. get isAlignLeft() {
  59. return (this._flags & Flags.LEFT) > 0;
  60. }
  61. set isAlignLeft(v) {
  62. this.setFlag(Flags.LEFT, v);
  63. }
  64. @property({ tooltip: EDITOR ? "是否对齐右边" : "" })
  65. get isAlignRight() {
  66. return (this._flags & Flags.RIGHT) > 0;
  67. }
  68. set isAlignRight(v) {
  69. this.setFlag(Flags.RIGHT, v);
  70. }
  71. @property
  72. _top = 0;
  73. @property({
  74. visible: function (this: AdapterSafeArea) {
  75. return this.isAlignTop;
  76. }, tooltip: EDITOR ? "本节点顶边和父节点顶边的距离,可填写负值,只有在 isAlignTop 开启时才有作用" : ""
  77. })
  78. get top() {
  79. return this._top;
  80. }
  81. set top(v) {
  82. if (this._top == v) {
  83. return;
  84. }
  85. this._top = v;
  86. this._isDirty = true;
  87. }
  88. @property
  89. _bottom = 0;
  90. @property({
  91. visible: function (this: AdapterSafeArea) {
  92. return this.isAlignBottom;
  93. }, tooltip: EDITOR ? "本节点顶边和父节点底边的距离,可填写负值,只有在 isAlignBottom 开启时才有作用" : ""
  94. })
  95. get bottom() {
  96. return this._bottom;
  97. }
  98. set bottom(v) {
  99. if (this._bottom == v) {
  100. return;
  101. }
  102. this._bottom = v;
  103. this._isDirty = true;
  104. }
  105. @property
  106. _left = 0;
  107. @property({
  108. visible: function (this: AdapterSafeArea) {
  109. return this.isAlignLeft;
  110. }, tooltip: EDITOR ? "本节点顶边和父节点左边的距离,可填写负值,只有在 isAlignLeft 开启时才有作用" : ""
  111. })
  112. get left() {
  113. return this._left;
  114. }
  115. set left(v) {
  116. if (this._left == v) {
  117. return;
  118. }
  119. this._left = v;
  120. this._isDirty = true;
  121. }
  122. @property
  123. _right = 0;
  124. @property({
  125. visible: function (this: AdapterSafeArea) {
  126. return this.isAlignRight;
  127. }, tooltip: EDITOR ? "本节点顶边和父节点右边的距离,可填写负值,只有在 isAlignRight 开启时才有作用" : ""
  128. })
  129. get right() {
  130. return this._right;
  131. }
  132. set right(v) {
  133. if (this._right == v) {
  134. return;
  135. }
  136. this._right = v;
  137. this._isDirty = true;
  138. }
  139. protected _isDirty = false;
  140. protected get widget() {
  141. let comp = this.getComponent(Widget);
  142. if (comp) {
  143. return comp;
  144. }
  145. return this.addComponent(Widget);
  146. }
  147. resetInEditor() {
  148. this.doLayout(true);
  149. }
  150. protected doLayout(isForce = false) {
  151. if (this._isDirty || isForce) {
  152. let widget = this.widget!;
  153. if (EDITOR) {
  154. widget.left = this.left;
  155. widget.right = this.right;
  156. widget.top = this.top;
  157. widget.bottom = this.bottom;
  158. return;
  159. }
  160. if (!this.isFullScreenAdaption) {
  161. return;
  162. }
  163. if (!widget || !widget.enabled) {
  164. return;
  165. }
  166. // 屏幕向上时,加上安全区域高度
  167. if (widget.isAlignTop && this.isAlignTop) {
  168. widget.isAbsoluteTop = true;
  169. if (this.direction == Adapter.direction.Portrait) {
  170. widget.top = this.top + Adapter.safeArea.outside.height;
  171. } else {
  172. widget.top = this.top;
  173. }
  174. }
  175. // 屏幕向下时,加上安全区域高度
  176. if (widget.isAlignBottom && this.isAlignBottom) {
  177. widget.isAbsoluteBottom = true;
  178. if (this.direction == Adapter.direction.UpsideDown) {
  179. widget.bottom = this.bottom + Adapter.safeArea.outside.height;
  180. } else {
  181. widget.bottom = this.bottom;
  182. }
  183. }
  184. // 屏幕向左时,加上安全区域宽度
  185. if (widget.isAlignLeft && this.isAlignLeft) {
  186. widget.isAbsoluteLeft = true;
  187. if (this.direction == Adapter.direction.LandscapeLeft) {
  188. widget.left = this.left + Adapter.safeArea.outside.width;
  189. } else {
  190. widget.left = this.left;
  191. }
  192. }
  193. // 屏幕向右时,加上安全区域宽度
  194. if (widget.isAlignRight && this.isAlignRight) {
  195. widget.isAbsoluteRight = true;
  196. if (this.direction == Adapter.direction.LandscapeRight) {
  197. widget.right = this.right + Adapter.safeArea.outside.width;
  198. } else {
  199. widget.right = this.right;
  200. }
  201. }
  202. widget.updateAlignment();
  203. this._isDirty = false;
  204. this.doOnAdapterComplete();
  205. }
  206. }
  207. protected onChangeSize() {
  208. this.doLayout(true);
  209. }
  210. protected update(dt: number) {
  211. super.update && super.update(dt);
  212. this.doLayout();
  213. }
  214. }