propertyTabComponent.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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 '../../sharedComponents/lineContainerComponent';
  6. import { FileButtonLineComponent } from '../../sharedUiComponents/lines/fileButtonLineComponent';
  7. import { Tools } from 'babylonjs/Misc/tools';
  8. import { SerializationTools } from '../../serializationTools';
  9. import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
  10. import { DataStorage } from 'babylonjs/Misc/dataStorage';
  11. import { GUINode } from '../../diagram/guiNode';
  12. import { SliderLineComponent } from '../../sharedComponents/sliderLineComponent';
  13. import { Engine } from 'babylonjs/Engines/engine';
  14. import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
  15. import { Observer } from 'babylonjs/Misc/observable';
  16. import { TextLineComponent } from "../../sharedUiComponents/lines/textLineComponent";
  17. import { StringTools } from "../../stringTools";
  18. import { AdvancedDynamicTexture } from "babylonjs-gui/2D/index";
  19. //import { SceneExplorerComponent } from "../sceneExplorer/sceneExplorerComponent";
  20. require("./propertyTab.scss");
  21. interface IPropertyTabComponentProps {
  22. globalState: GlobalState;
  23. }
  24. interface IPropertyTabComponentState {
  25. currentNode: Nullable<GUINode>;
  26. }
  27. export class PropertyTabComponent extends React.Component<IPropertyTabComponentProps, IPropertyTabComponentState> {
  28. private _onBuiltObserver: Nullable<Observer<void>>;
  29. constructor(props: IPropertyTabComponentProps) {
  30. super(props);
  31. this.state = { currentNode: null};
  32. }
  33. componentDidMount() {
  34. this.props.globalState.onSelectionChangedObservable.add((selection) => {
  35. if (selection instanceof GUINode) {
  36. this.setState({ currentNode: selection});
  37. } else {
  38. this.setState({ currentNode: null });
  39. }
  40. });
  41. this._onBuiltObserver = this.props.globalState.onBuiltObservable.add(() => {
  42. this.forceUpdate();
  43. });
  44. }
  45. componentWillUnmount() {
  46. this.props.globalState.onBuiltObservable.remove(this._onBuiltObserver);
  47. }
  48. processInputBlockUpdate(ib: InputBlock) {
  49. this.props.globalState.onUpdateRequiredObservable.notifyObservers();
  50. if (ib.isConstant) {
  51. this.props.globalState.onRebuildRequiredObservable.notifyObservers();
  52. }
  53. }
  54. load(file: File) {
  55. Tools.ReadFile(file, (data) => {
  56. let decoder = new TextDecoder("utf-8");
  57. SerializationTools.Deserialize(JSON.parse(decoder.decode(data)), this.props.globalState);
  58. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  59. }, undefined, true);
  60. }
  61. save() {
  62. let json = JSON.stringify(this.props.globalState.guiTexture.serializeContent());
  63. StringTools.DownloadAsFile(this.props.globalState.hostDocument, json, "guiTexture.json");
  64. }
  65. customSave() {
  66. /*this.props.globalState.onLogRequiredObservable.notifyObservers({message: "Saving your material to Babylon.js snippet server...", isError: false});
  67. this.props.globalState.customSave!.action(SerializationTools.Serialize(this.props.globalState.nodeMaterial, this.props.globalState)).then(() => {
  68. this.props.globalState.onLogRequiredObservable.notifyObservers({message: "Material saved successfully", isError: false});
  69. }).catch((err) => {
  70. this.props.globalState.onLogRequiredObservable.notifyObservers({message: err, isError: true});
  71. });*/
  72. }
  73. saveToSnippetServer() {
  74. var adt = this.props.globalState.guiTexture;
  75. let content = JSON.stringify(adt.serializeContent());
  76. var xmlHttp = new XMLHttpRequest();
  77. xmlHttp.onreadystatechange = () => {
  78. if (xmlHttp.readyState == 4) {
  79. if (xmlHttp.status == 200) {
  80. var snippet = JSON.parse(xmlHttp.responseText);
  81. const oldId = adt.snippetId || "_BLANK";
  82. adt.snippetId = snippet.id;
  83. if (snippet.version && snippet.version != "0") {
  84. adt.snippetId += "#" + snippet.version;
  85. }
  86. this.forceUpdate();
  87. if (navigator.clipboard) {
  88. navigator.clipboard.writeText(adt.snippetId);
  89. }
  90. let windowAsAny = window as any;
  91. if (windowAsAny.Playground && oldId) {
  92. windowAsAny.Playground.onRequestCodeChangeObservable.notifyObservers({
  93. regex: new RegExp(`parseFromSnippetAsync\\("${oldId}`, "g"),
  94. replace: `parseFromSnippetAsync("${adt.snippetId}`
  95. });
  96. }
  97. alert("GUI saved with ID: " + adt.snippetId + " (please note that the id was also saved to your clipboard)");
  98. }
  99. else {
  100. alert("Unable to save your GUI");
  101. }
  102. }
  103. }
  104. xmlHttp.open("POST", AdvancedDynamicTexture.SnippetUrl + (adt.snippetId ? "/" + adt.snippetId : ""), true);
  105. xmlHttp.setRequestHeader("Content-Type", "application/json");
  106. var dataToSend = {
  107. payload : JSON.stringify({
  108. gui: content
  109. }),
  110. name: "",
  111. description: "",
  112. tags: ""
  113. };
  114. xmlHttp.send(JSON.stringify(dataToSend));
  115. }
  116. loadFromSnippet() {
  117. let snippedID = window.prompt("Please enter the snippet ID to use");
  118. if (!snippedID) {
  119. return;
  120. }
  121. this.props.globalState.onSelectionChangedObservable.notifyObservers(null);
  122. this.props.globalState.guiTexture.parseFromSnippetAsync(snippedID);
  123. }
  124. render() {
  125. //var myScene=this.props.globalState.guiTexture.getScene();
  126. if (this.state.currentNode) {
  127. return (
  128. <div id="propertyTab">
  129. <div id="header">
  130. <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
  131. <div id="title">
  132. GUI EDITOR
  133. </div>
  134. </div>
  135. {//myScene && <SceneExplorerComponent globalState={this.props.globalState} scene={myScene}></SceneExplorerComponent>
  136. }
  137. {this.state.currentNode?.renderContainer()}
  138. {this.state.currentNode?.renderProperties()}
  139. </div>
  140. );
  141. }
  142. let gridSize = DataStorage.ReadNumber("GridSize", 20);
  143. return (
  144. <div id="propertyTab">
  145. <div id="header">
  146. <img id="logo" src="https://www.babylonjs.com/Assets/logo-babylonjs-social-twitter.png" />
  147. <div id="title">
  148. GUI EDITOR
  149. </div>
  150. </div>
  151. <div>
  152. <LineContainerComponent title="GENERAL">
  153. <TextLineComponent label="Version" value={Engine.Version}/>
  154. <TextLineComponent label="Help" value="doc.babylonjs.com" underline={true} onLink={() => window.open('https://doc.babylonjs.com/how_to/node_material', '_blank')}/>
  155. <ButtonLineComponent label="Reset to default" onClick={() => {
  156. this.props.globalState.onResetRequiredObservable.notifyObservers();
  157. }} />
  158. </LineContainerComponent>
  159. <LineContainerComponent title="UI">
  160. <ButtonLineComponent label="Zoom to fit" onClick={() => {
  161. this.props.globalState.onZoomToFitRequiredObservable.notifyObservers();
  162. }} />
  163. <ButtonLineComponent label="Reorganize" onClick={() => {
  164. this.props.globalState.onReOrganizedRequiredObservable.notifyObservers();
  165. }} />
  166. </LineContainerComponent>
  167. <LineContainerComponent title="OPTIONS">
  168. <CheckBoxLineComponent label="Embed textures when saving"
  169. isSelected={() => DataStorage.ReadBoolean("EmbedTextures", true)}
  170. onSelect={(value: boolean) => {
  171. DataStorage.WriteBoolean("EmbedTextures", value);
  172. }}
  173. />
  174. <SliderLineComponent label="Grid size" minimum={0} maximum={100} step={5}
  175. decimalCount={0} globalState={this.props.globalState}
  176. directValue={gridSize}
  177. onChange={(value) => {
  178. DataStorage.WriteNumber("GridSize", value);
  179. this.props.globalState.onGridSizeChanged.notifyObservers();
  180. this.forceUpdate();
  181. }}
  182. />
  183. <CheckBoxLineComponent label="Show grid"
  184. isSelected={() => DataStorage.ReadBoolean("ShowGrid", true)}
  185. onSelect={(value: boolean) => {
  186. DataStorage.WriteBoolean("ShowGrid", value);
  187. this.props.globalState.onGridSizeChanged.notifyObservers();
  188. }}
  189. />
  190. </LineContainerComponent>
  191. <LineContainerComponent title="FILE">
  192. <FileButtonLineComponent label="Load" onClick={(file) => this.load(file)} accept=".json" />
  193. <ButtonLineComponent label="Save" onClick={() => {
  194. this.save();
  195. }} />
  196. {
  197. this.props.globalState.customSave &&
  198. <ButtonLineComponent label={this.props.globalState.customSave!.label} onClick={() => {
  199. this.customSave();
  200. }} />
  201. }
  202. </LineContainerComponent>
  203. {
  204. <LineContainerComponent title="SNIPPET">
  205. <ButtonLineComponent label="Load from snippet server" onClick={() => this.loadFromSnippet()} />
  206. <ButtonLineComponent label="Save to snippet server" onClick={() => {
  207. this.saveToSnippetServer();
  208. }} />
  209. </LineContainerComponent>
  210. }
  211. </div>
  212. </div>
  213. );
  214. }
  215. }