propertyTabComponent.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import * as React from "react";
  2. import { GlobalState } from "../../globalState";
  3. import { Nullable } from "babylonjs/types";
  4. import { ButtonLineComponent } from "../../sharedUiComponents/lines/buttonLineComponent";
  5. import { LineContainerComponent } from "../../sharedUiComponents/lines/lineContainerComponent";
  6. import { FileButtonLineComponent } from "../../sharedUiComponents/lines/fileButtonLineComponent";
  7. import { Tools } from "babylonjs/Misc/tools";
  8. import { CheckBoxLineComponent } from "../../sharedUiComponents/lines/checkBoxLineComponent";
  9. import { DataStorage } from "babylonjs/Misc/dataStorage";
  10. import { GUINode } from "../../diagram/guiNode";
  11. import { Observer } from "babylonjs/Misc/observable";
  12. import { TextLineComponent } from "../../sharedUiComponents/lines/textLineComponent";
  13. import { StringTools } from "../../sharedUiComponents/stringTools";
  14. import { Engine } from "babylonjs/Engines/engine";
  15. import { LockObject } from "../../sharedUiComponents/tabs/propertyGrids/lockObject";
  16. import { SliderPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/sliderPropertyGridComponent";
  17. import { Slider } from "babylonjs-gui/2D/controls/sliders/slider";
  18. import { LinePropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/linePropertyGridComponent";
  19. import { RadioButtonPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/radioButtonPropertyGridComponent";
  20. import { TextBlock } from "babylonjs-gui/2D/controls/textBlock";
  21. import { InputText } from "babylonjs-gui/2D/controls/inputText";
  22. import { ColorPicker } from "babylonjs-gui/2D/controls/colorpicker";
  23. import { Image } from "babylonjs-gui/2D/controls/image";
  24. import { ImageBasedSlider } from "babylonjs-gui/2D/controls/sliders/imageBasedSlider";
  25. import { Rectangle } from "babylonjs-gui/2D/controls/rectangle";
  26. import { Ellipse } from "babylonjs-gui/2D/controls/ellipse";
  27. import { Checkbox } from "babylonjs-gui/2D/controls/checkbox";
  28. import { RadioButton } from "babylonjs-gui/2D/controls/radioButton";
  29. import { Line } from "babylonjs-gui/2D/controls/line";
  30. import { ScrollViewer } from "babylonjs-gui/2D/controls/scrollViewers/scrollViewer";
  31. import { Grid } from "babylonjs-gui/2D/controls/grid";
  32. import { StackPanel } from "babylonjs-gui/2D/controls/stackPanel";
  33. import { TextBlockPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/textBlockPropertyGridComponent";
  34. import { InputTextPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/inputTextPropertyGridComponent";
  35. import { ColorPickerPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/colorPickerPropertyGridComponent";
  36. import { ImagePropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/imagePropertyGridComponent";
  37. import { ImageBasedSliderPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/imageBasedSliderPropertyGridComponent";
  38. import { RectanglePropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/rectanglePropertyGridComponent";
  39. import { StackPanelPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/stackPanelPropertyGridComponent";
  40. import { GridPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/gridPropertyGridComponent";
  41. import { ScrollViewerPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/scrollViewerPropertyGridComponent";
  42. import { EllipsePropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/ellipsePropertyGridComponent";
  43. import { CheckboxPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/checkboxPropertyGridComponent";
  44. import { Control } from "babylonjs-gui/2D/controls/control";
  45. import { ControlPropertyGridComponent } from "../../sharedUiComponents/tabs/propertyGrids/gui/controlPropertyGridComponent";
  46. import { AdvancedDynamicTexture } from "babylonjs-gui/2D/advancedDynamicTexture";
  47. require("./propertyTab.scss");
  48. interface IPropertyTabComponentProps {
  49. globalState: GlobalState;
  50. }
  51. interface IPropertyTabComponentState {
  52. currentNode: Nullable<GUINode>;
  53. }
  54. export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, IPropertyTabComponentState> {
  55. private _onBuiltObserver: Nullable<Observer<void>>;
  56. private _timerIntervalId: number;
  57. private _lockObject = new LockObject();
  58. constructor(props: IPropertyTabComponentProps) {
  59. super(props);
  60. this.state = { currentNode: null };
  61. }
  62. timerRefresh() {
  63. if (!this._lockObject.lock) {
  64. this.forceUpdate();
  65. }
  66. }
  67. componentDidMount() {
  68. this._timerIntervalId = window.setInterval(() => this.timerRefresh(), 500);
  69. this.props.globalState.onSelectionChangedObservable.add((selection) => {
  70. if (selection instanceof GUINode) {
  71. this.setState({ currentNode: selection });
  72. } else {
  73. this.setState({ currentNode: null });
  74. }
  75. });
  76. this._onBuiltObserver = this.props.globalState.onBuiltObservable.add(() => {
  77. this.forceUpdate();
  78. });
  79. }
  80. componentWillUnmount() {
  81. window.clearInterval(this._timerIntervalId);
  82. this.props.globalState.onBuiltObservable.remove(this._onBuiltObserver);
  83. }
  84. load(file: File) {
  85. Tools.ReadFile(
  86. file,
  87. (data) => {
  88. const decoder = new TextDecoder("utf-8");
  89. this.props.globalState.workbench.loadFromJson(JSON.parse(decoder.decode(data)));
  90. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  91. },
  92. undefined,
  93. true
  94. );
  95. }
  96. save() {
  97. const json = JSON.stringify(this.props.globalState.guiTexture.serializeContent());
  98. StringTools.DownloadAsFile(this.props.globalState.hostDocument, json, "guiTexture.json");
  99. }
  100. saveToSnippetServer() {
  101. const adt = this.props.globalState.guiTexture;
  102. const content = JSON.stringify(adt.serializeContent());
  103. const xmlHttp = new XMLHttpRequest();
  104. xmlHttp.onreadystatechange = () => {
  105. if (xmlHttp.readyState == 4) {
  106. if (xmlHttp.status == 200) {
  107. const snippet = JSON.parse(xmlHttp.responseText);
  108. const oldId = adt.snippetId;
  109. adt.snippetId = snippet.id;
  110. if (snippet.version && snippet.version != "0") {
  111. adt.snippetId += "#" + snippet.version;
  112. }
  113. this.forceUpdate();
  114. if (navigator.clipboard) {
  115. navigator.clipboard.writeText(adt.snippetId);
  116. }
  117. const windowAsAny = window as any;
  118. if (windowAsAny.Playground && oldId) {
  119. windowAsAny.Playground.onRequestCodeChangeObservable.notifyObservers({
  120. regex: new RegExp(oldId, "g"),
  121. replace: `parseFromSnippetAsync("${adt.snippetId}`,
  122. });
  123. }
  124. alert("GUI saved with ID: " + adt.snippetId + " (please note that the id was also saved to your clipboard)");
  125. } else {
  126. alert("Unable to save your GUI");
  127. }
  128. }
  129. };
  130. xmlHttp.open("POST", AdvancedDynamicTexture.SnippetUrl + (adt.snippetId ? "/" + adt.snippetId : ""), true);
  131. xmlHttp.setRequestHeader("Content-Type", "application/json");
  132. const dataToSend = {
  133. payload: JSON.stringify({
  134. gui: content,
  135. }),
  136. name: "",
  137. description: "",
  138. tags: "",
  139. };
  140. xmlHttp.send(JSON.stringify(dataToSend));
  141. }
  142. loadFromSnippet() {
  143. const snippedID = window.prompt("Please enter the snippet ID to use");
  144. if (!snippedID) {
  145. return;
  146. }
  147. this.props.globalState.workbench.loadFromSnippet(snippedID);
  148. }
  149. renderProperties() {
  150. const className = this.state.currentNode?.guiControl.getClassName();
  151. switch (className) {
  152. case "TextBlock": {
  153. const textBlock = this.state.currentNode?.guiControl as TextBlock;
  154. return <TextBlockPropertyGridComponent textBlock={textBlock} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  155. }
  156. case "InputText": {
  157. const inputText = this.state.currentNode?.guiControl as InputText;
  158. return <InputTextPropertyGridComponent inputText={inputText} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  159. }
  160. case "ColorPicker": {
  161. const colorPicker = this.state.currentNode?.guiControl as ColorPicker;
  162. return <ColorPickerPropertyGridComponent colorPicker={colorPicker} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  163. }
  164. case "Image": {
  165. const image = this.state.currentNode?.guiControl as Image;
  166. return <ImagePropertyGridComponent image={image} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  167. }
  168. case "Slider": {
  169. const slider = this.state.currentNode?.guiControl as Slider;
  170. return <SliderPropertyGridComponent slider={slider} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  171. }
  172. case "ImageBasedSlider": {
  173. const imageBasedSlider = this.state.currentNode?.guiControl as ImageBasedSlider;
  174. return <ImageBasedSliderPropertyGridComponent imageBasedSlider={imageBasedSlider} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  175. }
  176. case "Rectangle": {
  177. const rectangle = this.state.currentNode?.guiControl as Rectangle;
  178. return <RectanglePropertyGridComponent rectangle={rectangle} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  179. }
  180. case "StackPanel": {
  181. const stackPanel = this.state.currentNode?.guiControl as StackPanel;
  182. return <StackPanelPropertyGridComponent stackPanel={stackPanel} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  183. }
  184. case "Grid": {
  185. const grid = this.state.currentNode?.guiControl as Grid;
  186. return <GridPropertyGridComponent grid={grid} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  187. }
  188. case "ScrollViewer": {
  189. const scrollViewer = this.state.currentNode?.guiControl as ScrollViewer;
  190. return <ScrollViewerPropertyGridComponent scrollViewer={scrollViewer} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  191. }
  192. case "Ellipse": {
  193. const ellipse = this.state.currentNode?.guiControl as Ellipse;
  194. return <EllipsePropertyGridComponent ellipse={ellipse} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  195. }
  196. case "Checkbox": {
  197. const checkbox = this.state.currentNode?.guiControl as Checkbox;
  198. return <CheckboxPropertyGridComponent checkbox={checkbox} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  199. }
  200. case "RadioButton": {
  201. const radioButton = this.state.currentNode?.guiControl as RadioButton;
  202. return <RadioButtonPropertyGridComponent radioButton={radioButton} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  203. }
  204. case "Line": {
  205. const line = this.state.currentNode?.guiControl as Line;
  206. return <LinePropertyGridComponent line={line} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  207. }
  208. }
  209. if (className !== "") {
  210. const control = this.state.currentNode?.guiControl as Control;
  211. return <ControlPropertyGridComponent control={control} lockObject={this._lockObject} onPropertyChangedObservable={this.props.globalState.onPropertyChangedObservable} />;
  212. }
  213. return null;
  214. }
  215. render() {
  216. if (this.state.currentNode) {
  217. return (
  218. <div id="propertyTab">
  219. <div id="header" >
  220. <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
  221. <div id="title">GUI EDITOR</div>
  222. </div>
  223. {this.renderProperties()}
  224. <ButtonLineComponent label="DELETE GUI" onClick={() => {
  225. this.state.currentNode?.dispose();
  226. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  227. }} />
  228. </div>
  229. );
  230. }
  231. return (
  232. <div id="propertyTab">
  233. <div id="header">
  234. <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
  235. <div id="title">GUI EDITOR</div>
  236. </div>
  237. <div>
  238. <LineContainerComponent title="GENERAL">
  239. <TextLineComponent label="Version" value={Engine.Version} />
  240. <TextLineComponent label="Help" value="doc.babylonjs.com" underline={true} onLink={() => window.open("https://doc.babylonjs.com", "_blank")} />
  241. <ButtonLineComponent
  242. label="Reset to default"
  243. onClick={() => {
  244. this.props.globalState.onResetRequiredObservable.notifyObservers();
  245. }}
  246. />
  247. </LineContainerComponent>
  248. <LineContainerComponent title="OPTIONS">
  249. <CheckBoxLineComponent
  250. label="Show grid"
  251. isSelected={() => DataStorage.ReadBoolean("ShowGrid", true)}
  252. onSelect={(value: boolean) => {
  253. DataStorage.WriteBoolean("ShowGrid", value);
  254. }}
  255. />
  256. </LineContainerComponent>
  257. <LineContainerComponent title="FILE">
  258. <FileButtonLineComponent label="Load" onClick={(file) => this.load(file)} accept=".json" />
  259. <ButtonLineComponent
  260. label="Save"
  261. onClick={() => {
  262. this.save();
  263. }}
  264. />
  265. </LineContainerComponent>
  266. {
  267. <LineContainerComponent title="SNIPPET">
  268. <ButtonLineComponent label="Load from snippet server" onClick={() => this.loadFromSnippet()} />
  269. <ButtonLineComponent
  270. label="Save to snippet server"
  271. onClick={() => {
  272. this.saveToSnippetServer();
  273. }}
  274. />
  275. </LineContainerComponent>
  276. }
  277. </div>
  278. </div>
  279. );
  280. }
  281. }