toggleButton.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import { Nullable } from "babylonjs/types";
  2. import { Observable } from "babylonjs/Misc/observable";
  3. import { Vector2 } from "babylonjs/Maths/math.vector";
  4. import { Rectangle } from "./rectangle";
  5. import { Control } from "./control";
  6. import { TextBlock } from "./textBlock";
  7. import { Image } from "./image";
  8. import { _TypeStore } from "babylonjs/Misc/typeStore";
  9. import { PointerInfoBase } from "babylonjs/Events/pointerEvents";
  10. /**
  11. * Class used to create toggle buttons
  12. */
  13. export class ToggleButton extends Rectangle {
  14. /**
  15. * Function called to generate the toActive animation
  16. */
  17. public toActiveAnimation: () => void;
  18. /**
  19. * Function called to generate the toInactive animation
  20. */
  21. public toInactiveAnimation: () => void;
  22. /**
  23. * Function called to generate a pointer enter animation when the toggle button is active.
  24. */
  25. public pointerEnterActiveAnimation: () => void;
  26. /**
  27. * Function called to generate a pointer out animation when the toggle button is active.
  28. */
  29. public pointerOutActiveAnimation: () => void;
  30. /**
  31. * Function called to generate a pointer down animation when the toggle button is active.
  32. */
  33. public pointerDownActiveAnimation: () => void;
  34. /**
  35. * Function called to generate a pointer up animation when the toggle button is active.
  36. */
  37. public pointerUpActiveAnimation: () => void;
  38. /**
  39. * Function called to generate a pointer enter animation when the toggle button is inactive.
  40. */
  41. public pointerEnterInactiveAnimation: () => void;
  42. /**
  43. * Function called to generate a pointer out animation when the toggle button is inactive.
  44. */
  45. public pointerOutInactiveAnimation: () => void;
  46. /**
  47. * Function called to generate a pointer down animation when the toggle button is inactive.
  48. */
  49. public pointerDownInactiveAnimation: () => void;
  50. /**
  51. * Function called to generate a pointer up animation when the toggle button is inactive.
  52. */
  53. public pointerUpInactiveAnimation: () => void;
  54. /** Observable raised when isActive is changed */
  55. public onIsActiveChangedObservable = new Observable<boolean>();
  56. /**
  57. * Gets or sets a boolean indicating that the toggle button will let internal controls handle picking instead of doing it directly using its bounding info
  58. */
  59. public delegatePickingToChildren = false;
  60. private _image: Nullable<Image>;
  61. /**
  62. * Returns the ToggleButton's image control if it exists
  63. */
  64. public get image(): Nullable<Image> {
  65. return this._image;
  66. }
  67. private _textBlock: Nullable<TextBlock>;
  68. /**
  69. * Returns the ToggleButton's child TextBlock control if it exists
  70. */
  71. public get textBlock(): Nullable<TextBlock> {
  72. return this._textBlock;
  73. }
  74. private _group: string;
  75. /** Gets or sets group name this toggle button belongs to */
  76. public get group(): string {
  77. return this._group;
  78. }
  79. public set group(value: string) {
  80. if (this._group === value) {
  81. return;
  82. }
  83. this._group = value;
  84. }
  85. private _isActive = false;
  86. /** Gets or sets a boolean indicating if the toogle button is active or not */
  87. public get isActive(): boolean {
  88. return this._isActive;
  89. }
  90. public set isActive(value: boolean) {
  91. // Function modeled after radioButton.ts
  92. if (this._isActive === value) {
  93. return;
  94. }
  95. this._isActive = value;
  96. // Update the visual state based on the new value
  97. if (this._isActive) {
  98. if (this.toActiveAnimation) {
  99. this.toActiveAnimation();
  100. }
  101. } else {
  102. if (this.toInactiveAnimation) {
  103. this.toInactiveAnimation?.();
  104. }
  105. }
  106. this._markAsDirty();
  107. this.onIsActiveChangedObservable.notifyObservers(value);
  108. if (this._isActive && this._host) {
  109. // Update all controls from same group
  110. this._host.executeOnAllControls((control) => {
  111. if (control == this) {
  112. return;
  113. }
  114. if ((<any>control).group === undefined) {
  115. return;
  116. }
  117. const childToggle = <ToggleButton>control;
  118. // A toggle group can only have 1 active element at a given time. So if this toggle has a group, we need to ensure other toggles in this group get set to inactive.
  119. if (childToggle.group === this.group) {
  120. childToggle.isActive = false; // Set other toggles in group as inactive
  121. }
  122. });
  123. }
  124. }
  125. /**
  126. * Creates a new ToggleButton
  127. * @param name defines the control name
  128. * @param group defines the toggle group this toggle belongs to
  129. */
  130. constructor(public name?: string, group?: string) {
  131. super(name);
  132. this.group = group ?? "";
  133. this.thickness = 0;
  134. this.isPointerBlocker = true;
  135. let alphaStore: Nullable<number> = null;
  136. this.toActiveAnimation = () => {
  137. this.thickness = 1;
  138. };
  139. this.toInactiveAnimation = () => {
  140. this.thickness = 0;
  141. };
  142. this.pointerEnterActiveAnimation = () => {
  143. alphaStore = this.alpha;
  144. this.alpha -= 0.1;
  145. };
  146. this.pointerOutActiveAnimation = () => {
  147. if (alphaStore !== null) {
  148. this.alpha = alphaStore;
  149. }
  150. };
  151. this.pointerDownActiveAnimation = () => {
  152. this.scaleX -= 0.05;
  153. this.scaleY -= 0.05;
  154. };
  155. this.pointerUpActiveAnimation = () => {
  156. this.scaleX += 0.05;
  157. this.scaleY += 0.05;
  158. };
  159. this.pointerEnterInactiveAnimation = () => {
  160. alphaStore = this.alpha;
  161. this.alpha -= 0.1;
  162. };
  163. this.pointerOutInactiveAnimation = () => {
  164. if (alphaStore !== null) {
  165. this.alpha = alphaStore;
  166. }
  167. };
  168. this.pointerDownInactiveAnimation = () => {
  169. this.scaleX -= 0.05;
  170. this.scaleY -= 0.05;
  171. };
  172. this.pointerUpInactiveAnimation = () => {
  173. this.scaleX += 0.05;
  174. this.scaleY += 0.05;
  175. };
  176. }
  177. protected _getTypeName(): string {
  178. return "ToggleButton";
  179. }
  180. // While being a container, the toggle button behaves like a control.
  181. /** @hidden */
  182. public _processPicking(x: number, y: number, pi: PointerInfoBase, type: number, pointerId: number, buttonIndex: number, deltaX?: number, deltaY?: number): boolean {
  183. if (!this._isEnabled || !this.isHitTestVisible || !this.isVisible || this.notRenderable) {
  184. return false;
  185. }
  186. if (!super.contains(x, y)) {
  187. return false;
  188. }
  189. if (this.delegatePickingToChildren) {
  190. let contains = false;
  191. for (var index = this._children.length - 1; index >= 0; index--) {
  192. var child = this._children[index];
  193. if (child.isEnabled && child.isHitTestVisible && child.isVisible && !child.notRenderable && child.contains(x, y)) {
  194. contains = true;
  195. break;
  196. }
  197. }
  198. if (!contains) {
  199. return false;
  200. }
  201. }
  202. this._processObservables(type, x, y, pi, pointerId, buttonIndex, deltaX, deltaY);
  203. return true;
  204. }
  205. /** @hidden */
  206. public _onPointerEnter(target: Control, pi: PointerInfoBase): boolean {
  207. if (!super._onPointerEnter(target, pi)) {
  208. return false;
  209. }
  210. if (this._isActive) {
  211. if (this.pointerEnterActiveAnimation) {
  212. this.pointerEnterActiveAnimation();
  213. }
  214. } else {
  215. if (this.pointerEnterInactiveAnimation) {
  216. this.pointerEnterInactiveAnimation();
  217. }
  218. }
  219. return true;
  220. }
  221. /** @hidden */
  222. public _onPointerOut(target: Control, pi: PointerInfoBase, force = false): void {
  223. if (this._isActive) {
  224. if (this.pointerOutActiveAnimation) {
  225. this.pointerOutActiveAnimation();
  226. }
  227. } else {
  228. if (this.pointerOutInactiveAnimation) {
  229. this.pointerOutInactiveAnimation();
  230. }
  231. }
  232. super._onPointerOut(target, pi, force);
  233. }
  234. /** @hidden */
  235. public _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, pi: PointerInfoBase): boolean {
  236. if (!super._onPointerDown(target, coordinates, pointerId, buttonIndex, pi)) {
  237. return false;
  238. }
  239. if (this._isActive) {
  240. if (this.pointerDownActiveAnimation) {
  241. this.pointerDownActiveAnimation();
  242. }
  243. } else {
  244. if (this.pointerDownInactiveAnimation) {
  245. this.pointerDownInactiveAnimation();
  246. }
  247. }
  248. return true;
  249. }
  250. /** @hidden */
  251. public _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean, pi: PointerInfoBase): void {
  252. if (this._isActive) {
  253. if (this.pointerUpActiveAnimation) {
  254. this.pointerUpActiveAnimation();
  255. }
  256. } else {
  257. if (this.pointerUpInactiveAnimation) {
  258. this.pointerUpInactiveAnimation();
  259. }
  260. }
  261. super._onPointerUp(target, coordinates, pointerId, buttonIndex, notifyClick, pi);
  262. }
  263. }
  264. _TypeStore.RegisteredTypes["BABYLON.GUI.ToggleButton"] = ToggleButton;