PropsEditor.hx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. package hide.comp;
  2. import hide.comp.GradientEditor.GradientBox;
  3. import hrt.impl.TextureType.Utils;
  4. import hrt.prefab.Props;
  5. class PropsEditor extends Component {
  6. public var undo : hide.ui.UndoHistory;
  7. public var lastChange : Float = 0.;
  8. public var fields(default, null) : Array<PropsField>;
  9. public var groups(default, null) : Map<String, Array<PropsField>>;
  10. public var isTempChange = false;
  11. public function new(?undo,?parent,?el) {
  12. super(parent,el);
  13. element.addClass("hide-properties");
  14. this.undo = undo == null ? new hide.ui.UndoHistory() : undo;
  15. fields = [];
  16. groups = new Map();
  17. }
  18. public function clear() {
  19. element.empty();
  20. fields = [];
  21. groups = new Map();
  22. }
  23. public function onDragDrop( items : Array<String>, isDrop : Bool ) : Bool {
  24. if( items.length == 0 )
  25. return false;
  26. var pickedEl = js.Browser.document.elementFromPoint(ide.mouseX, ide.mouseY);
  27. var rootEl = element[0];
  28. while( pickedEl != null ) {
  29. if( pickedEl == rootEl )
  30. return false;
  31. for( field in fields ) {
  32. if( field.tselect != null && field.tselect.element[0] == pickedEl )
  33. return field.tselect.onDragDrop(items, isDrop);
  34. if( field.fselect != null && field.fselect.element[0] == pickedEl )
  35. return field.fselect.onDragDrop(items, isDrop);
  36. }
  37. pickedEl = pickedEl.parentElement;
  38. }
  39. return false;
  40. }
  41. public function addMaterial( m : h3d.mat.Material, ?parent : Element, ?onChange ) {
  42. var def = m.editProps();
  43. def = add(def, m.props, function(name) {
  44. m.refreshProps();
  45. if( !isTempChange ) {
  46. def.remove();
  47. addMaterial(m, parent, onChange);
  48. if( onChange != null ) onChange(name);
  49. }
  50. });
  51. if( parent != null && parent.length != 0 )
  52. def.appendTo(parent);
  53. }
  54. public static function makePropEl(p: PropDef, parent: Element) {
  55. switch( p.t ) {
  56. case PInt(min, max):
  57. var e = new Element('<input type="range" field="${p.name}" step="1">').appendTo(parent);
  58. if( min != null ) e.attr("min", "" + min);
  59. if(p.def != null) e.attr("value", "" + p.def);
  60. e.attr("max", "" + (max == null ? 100 : max));
  61. case PFloat(min, max):
  62. var e = new Element('<input type="range" field="${p.name}">').appendTo(parent);
  63. if(p.def != null) e.attr("value", "" + p.def);
  64. if( min != null ) e.attr("min", "" + min);
  65. if( max != null ) e.attr("max", "" + max);
  66. case PBool:
  67. new Element('<input type="checkbox" field="${p.name}">').appendTo(parent);
  68. case PTexturePath:
  69. new Element('<input type="texturepath" field="${p.name}">').appendTo(parent);
  70. case PTexture:
  71. new Element('<input type="texturechoice" field="${p.name}">').appendTo(parent);
  72. case PGradient:
  73. new Element('<input type="gradient" field="${p.name}">').appendTo(parent);
  74. case PUnsupported(text):
  75. new Element('<font color="red">' + StringTools.htmlEscape(text) + '</font>').appendTo(parent);
  76. case PVec(n, min, max):
  77. var isColor = p.name.toLowerCase().indexOf("color") >= 0;
  78. if(isColor && (n == 3 || n == 4)) {
  79. new Element('<input type="color" field="${p.name}">').appendTo(parent);
  80. }
  81. else {
  82. var row = new Element('<div class="flex"/>').appendTo(parent);
  83. for( i in 0...n ) {
  84. var e = new Element('<input type="number" field="${p.name}.$i">').appendTo(row);
  85. if(min == null) min = isColor ? 0.0 : -1.0;
  86. if(max == null) max = 1.0;
  87. e.attr("min", "" + min);
  88. e.attr("max", "" + max);
  89. }
  90. }
  91. case PChoice(choices):
  92. var e = new Element('<select field="${p.name}" type="number"></select>').appendTo(parent);
  93. for(c in choices)
  94. new hide.Element('<option>').attr("value", choices.indexOf(c)).text(upperCase(c)).appendTo(e);
  95. case PEnum(en):
  96. var e = new Element('<select field="${p.name}"></select>').appendTo(parent);
  97. case PFile(exts):
  98. new Element('<input type="texturepath" extensions="${exts.join(" ")}" field="${p.name}">').appendTo(parent);
  99. case PString(len):
  100. var e = new Element('<input type="text" field="${p.name}">').appendTo(parent);
  101. if ( len != null ) e.attr("maxlength", "" + len);
  102. if ( p.def != null ) e.attr("value", "" + p.def);
  103. }
  104. }
  105. public static function makeGroupEl(name: String, content: Element) {
  106. var el = new Element('<div class="group" name="${name}"></div>');
  107. content.appendTo(el);
  108. return el;
  109. }
  110. public static function makeSectionEl(name: String, content: Element, ?headerContent: Element) {
  111. var el = new Element('<div class="section"><h1><span>${name}</span></h1><div class="content"></div></div>');
  112. if (headerContent != null) headerContent.appendTo(el.find("h1"));
  113. content.appendTo(el.find(".content"));
  114. return el;
  115. }
  116. public static function makeLabelEl(name: String, content: Element) {
  117. var el = new Element('<span><dt>${name}</dt><dd></dd></span>');
  118. content.appendTo(el.find("dd"));
  119. return el;
  120. }
  121. public static function makeListEl(content:Array<Element>) {
  122. var el = new Element("<dl>");
  123. for ( e in content ) e.appendTo(el);
  124. return el;
  125. }
  126. static function upperCase(prop: String) {
  127. return prop.charAt(0).toUpperCase() + prop.substr(1);
  128. }
  129. public static function makePropsList(props : Array<PropDef>) : Element {
  130. var e = new Element('<dl>');
  131. for( p in props ) {
  132. new Element('<dt>${p.disp != null ? p.disp : upperCase(p.name)}</dt>').appendTo(e);
  133. var def = new Element('<dd>').appendTo(e);
  134. makePropEl(p, def);
  135. }
  136. return e;
  137. }
  138. public function addProps( props : Array<PropDef>, context : Dynamic, ?onChange : String -> Void) {
  139. var e = makePropsList(props);
  140. return add(e, context, onChange);
  141. }
  142. public function add( e : Element, ?context : Dynamic, ?onChange : String -> Void ) {
  143. e.appendTo(element);
  144. return build(e,context,onChange);
  145. }
  146. public function build( e : Element, ?context : Dynamic, ?onChange : String -> Void ) {
  147. e = e.wrap("<div></div>").parent(); // necessary to have find working on top level element
  148. e.find("input[type=checkbox]").wrap("<div class='checkbox-wrapper'></div>");
  149. e.find("input[type=range]").not("[step]").attr({step: "any", tabindex:"-1"});
  150. // Wrap dt+dd for nw versions of 0.4x+
  151. for ( el in e.find("dt").wrap("<div></div>").parent().elements() ) {
  152. var n = el.next();
  153. if (n.length != 0 && n[0].tagName == "DD") n.appendTo(el);
  154. }
  155. // -- reload states ---
  156. for( h in e.find(".section > h1").elements() )
  157. if( getDisplayState("section:" + StringTools.trim(h.text())) != false )
  158. h.parent().addClass("open");
  159. // init section
  160. e.find(".section").not(".open").children(".content").hide();
  161. e.find(".section > h1").mousedown(function(e) {
  162. if( e.button != 0 ) return;
  163. var section = e.getThis().parent();
  164. section.toggleClass("open");
  165. section.children(".content").slideToggle(100);
  166. saveDisplayState("section:" + StringTools.trim(e.getThis().text()), section.hasClass("open"));
  167. }).find("input").mousedown(function(e) e.stopPropagation());
  168. e.find("input[type=section_name]").change(function(e) {
  169. e.getThis().closest(".section").find(">h1 span").text(e.getThis().val());
  170. });
  171. // init groups
  172. var gindex = 0;
  173. for( g in e.find(".group").elements() ) {
  174. var name = g.attr("name");
  175. g.wrapInner("<div class='content'></div>");
  176. if( name != null )
  177. new Element("<div class='title'>" + g.attr("name") + '</div>').prependTo(g);
  178. else {
  179. name = "_g"+(gindex++);
  180. g.attr("name",name);
  181. g.children().children(".title").prependTo(g);
  182. }
  183. var s = g.closest(".section");
  184. var key = (s.length == 0 ? "" : StringTools.trim(s.children("h1").text()) + "/") + name;
  185. if( getDisplayState("group:" + key) != false && !g.hasClass("closed") )
  186. g.addClass("open");
  187. }
  188. e.find(".group").not(".open").children(".content").hide();
  189. e.find(".group > .title").mousedown(function(e) {
  190. if( e.button != 0 ) return;
  191. var group = e.getThis().parent();
  192. group.toggleClass("open");
  193. group.children(".content").slideToggle(100);
  194. var s = group.closest(".section");
  195. var key = (s.length == 0 ? "" : StringTools.trim(s.children("h1").text()) + "/") + group.attr("name");
  196. saveDisplayState("group:" + key, group.hasClass("open"));
  197. }).find("input").mousedown(function(e) e.stopPropagation());
  198. e.find("input[type=group_name]").change(function(e) {
  199. e.getThis().closest(".group").find(">.title").val(e.getThis().val());
  200. });
  201. var groupFields = [];
  202. // init input reflection
  203. for( f in e.find("[field]").elements() ) {
  204. var f = new PropsField(this, f, context);
  205. f.onChange = function(undo) {
  206. isTempChange = f.isTempChange;
  207. lastChange = haxe.Timer.stamp();
  208. if( onChange != null ) onChange(@:privateAccess f.fname);
  209. isTempChange = false;
  210. };
  211. groupFields.push(f);
  212. fields.push(f);
  213. // Init reset buttons
  214. var def = f.element.attr("value");
  215. if(def != null) {
  216. var dd = f.element.parent().parent("dd");
  217. wrapDt(dd.prev("dt"), def, function(e) {
  218. var range = @:privateAccess f.range;
  219. if(range != null) {
  220. if(e.ctrlKey) {
  221. range.value = Math.round(range.value);
  222. range.onChange(false);
  223. }
  224. else
  225. range.reset();
  226. }
  227. });
  228. }
  229. }
  230. var groupName = e.find(".group").attr("name");
  231. groups.set(groupName, groupFields);
  232. return e;
  233. }
  234. public static function wrapDt(dt : Element, defValue : String, onClick : (e : js.jquery.Event) -> Void) {
  235. var tooltip = 'Click to reset ($defValue)\nCtrl+Click to round';
  236. var button = dt.wrapInner('<input type="button" tabindex="-1" value="${upperCase(dt.text())}" title="$tooltip"/>');
  237. button.click(onClick);
  238. }
  239. }
  240. @:allow(hide.comp.PropsEditor)
  241. class PropsField extends Component {
  242. public var fname : String;
  243. var isTempChange : Bool;
  244. var props : PropsEditor;
  245. var context : Dynamic;
  246. var current : Dynamic;
  247. var currentSave : Dynamic;
  248. var enumValue : Enum<Dynamic>;
  249. var tempChange : Bool;
  250. var beforeTempChange : { value : Dynamic };
  251. var tchoice : hide.comp.TextureChoice;
  252. var gradient : GradientBox;
  253. var multiRange : MultiRange;
  254. var tselect : hide.comp.TextureSelect;
  255. var fselect : hide.comp.FileSelect;
  256. var viewRoot : Element;
  257. var range : hide.comp.Range;
  258. var subfields : Array<String>;
  259. public function new(props, el, context) {
  260. super(null,el);
  261. viewRoot = element.closest(".lm_content");
  262. this.props = props;
  263. this.context = context;
  264. var f = element;
  265. Reflect.setField(f[0],"propsField", this);
  266. fname = f.attr("field");
  267. current = getFieldValue();
  268. switch( f.attr("type") ) {
  269. case "checkbox":
  270. f.prop("checked", current);
  271. f.mousedown(function(e) e.stopPropagation());
  272. f.change(function(_) {
  273. undo(function() {
  274. var f = resolveField();
  275. f.current = getFieldValue();
  276. f.element.prop("checked", f.current);
  277. f.onChange(true);
  278. });
  279. current = f.prop("checked");
  280. setFieldValue(current);
  281. onChange(false);
  282. });
  283. return;
  284. case "texture":
  285. tselect = new hide.comp.TextureSelect(null,f);
  286. tselect.value = current;
  287. tselect.onChange = function() {
  288. undo(function() {
  289. var f = resolveField();
  290. f.current = getFieldValue();
  291. f.tselect.value = f.current;
  292. f.onChange(true);
  293. });
  294. current = tselect.value;
  295. setFieldValue(current);
  296. onChange(false);
  297. }
  298. return;
  299. case "texturepath":
  300. tselect = new hide.comp.TextureSelect(null,f);
  301. tselect.path = current;
  302. tselect.onChange = function() {
  303. undo(function() {
  304. var f = resolveField();
  305. f.current = getFieldValue();
  306. f.tselect.path = f.current;
  307. f.onChange(true);
  308. });
  309. current = tselect.path;
  310. setFieldValue(current);
  311. onChange(false);
  312. }
  313. return;
  314. case "texturechoice":
  315. tchoice = new TextureChoice(null, f);
  316. tchoice.value = current;
  317. currentSave = Utils.copyTextureData(current);
  318. tchoice.onChange = function(shouldUndo : Bool) {
  319. if (shouldUndo) {
  320. var setVal = function(val, undo) {
  321. var f = resolveField();
  322. f.current = val;
  323. f.currentSave = Utils.copyTextureData(val);
  324. f.tchoice.value = val;
  325. setFieldValue(val);
  326. f.onChange(undo);
  327. }
  328. var oldVal = Utils.copyTextureData(currentSave);
  329. var newVal = Utils.copyTextureData(tchoice.value);
  330. props.undo.change(Custom(function(undo) {
  331. if (undo) {
  332. setVal(oldVal, true);
  333. } else {
  334. setVal(newVal, false);
  335. }
  336. }));
  337. setVal(tchoice.value, false);
  338. } else {
  339. current = tchoice.value;
  340. setFieldValue(current);
  341. onChange(false);
  342. }
  343. }
  344. return;
  345. case "gradient":
  346. gradient = new GradientBox(null, f);
  347. gradient.value = current;
  348. currentSave = Utils.copyTextureData(current);
  349. gradient.onChange = function(shouldUndo : Bool) {
  350. if (shouldUndo) {
  351. var setVal = function(val, undo) {
  352. var f = resolveField();
  353. f.current = val;
  354. f.currentSave = Utils.copyTextureData(val);
  355. f.gradient.value = val;
  356. setFieldValue(val);
  357. f.onChange(undo);
  358. }
  359. var oldVal = Utils.copyTextureData(currentSave);
  360. var newVal = Utils.copyTextureData(gradient.value);
  361. props.undo.change(Custom(function(undo) {
  362. if (undo) {
  363. setVal(oldVal, true);
  364. } else {
  365. setVal(newVal, false);
  366. }
  367. }));
  368. setVal(gradient.value, false);
  369. } else {
  370. current = gradient.value;
  371. setFieldValue(current);
  372. onChange(false);
  373. }
  374. }
  375. case "model":
  376. fselect = new hide.comp.FileSelect(["hmd", "fbx"], null, f);
  377. fselect.path = current;
  378. fselect.onChange = function() {
  379. undo(function() {
  380. var f = resolveField();
  381. f.current = getFieldValue();
  382. f.fselect.path = f.current;
  383. f.onChange(true);
  384. });
  385. current = fselect.path;
  386. setFieldValue(current);
  387. onChange(false);
  388. };
  389. return;
  390. case "fileselect":
  391. var exts = f.attr("extensions");
  392. if( exts == null ) exts = "*";
  393. fselect = new hide.comp.FileSelect(exts.split(" "), null, f);
  394. fselect.path = current;
  395. fselect.onChange = function() {
  396. undo(function() {
  397. var f = resolveField();
  398. f.current = getFieldValue();
  399. f.fselect.path = f.current;
  400. f.onChange(true);
  401. });
  402. current = fselect.path;
  403. setFieldValue(current);
  404. onChange(false);
  405. };
  406. return;
  407. case "range":
  408. range = new hide.comp.Range(null,f);
  409. if(!Math.isNaN(current))
  410. range.value = current;
  411. range.onChange = function(temp) {
  412. tempChange = temp;
  413. setVal(range.value);
  414. };
  415. return;
  416. case "multi-range":
  417. var subfieldStr = f.attr("data-subfields");
  418. subfields = subfieldStr.split(",");
  419. var name = f.parent().prev("dt").text();
  420. var parentDiv = f.parent().parent();
  421. parentDiv.empty();
  422. var multiRange = new hide.comp.MultiRange(parentDiv, f, subfields.length, [for (subfield in subfields) name + " " + subfield]);
  423. var a = getAccess();
  424. multiRange.value = [for (subfield in subfields) Reflect.getProperty(a.obj, a.name+subfield)];
  425. current = multiRange.value;
  426. currentSave = (cast current : Array<Float>).copy();
  427. multiRange.onChange = function(isTemporary : Bool) {
  428. var setVal = function(val : Array<Float>, undo, refreshComp) {
  429. var f = resolveField();
  430. var a = f.getAccess();
  431. f.current = val;
  432. f.currentSave = val.copy();
  433. for (i => subfield in subfields)
  434. Reflect.setProperty(a.obj, a.name+subfield, val[i]);
  435. if (refreshComp)
  436. multiRange.value = val;
  437. f.onChange(undo);
  438. };
  439. if (!isTemporary) {
  440. var arr : Array<Float> = cast currentSave;
  441. var oldVal = arr.copy();
  442. var newVal = multiRange.value.copy();
  443. props.undo.change(Custom(function(undo) {
  444. if (undo) {
  445. trace("Undo", oldVal, newVal);
  446. setVal(oldVal, true, true);
  447. } else {
  448. trace("Redo", newVal);
  449. setVal(newVal, false, true);
  450. }
  451. }));
  452. setVal(multiRange.value, false, false);
  453. }
  454. else {
  455. var a = getAccess();
  456. var val = multiRange.value;
  457. current = val;
  458. for (i => subfield in subfields)
  459. Reflect.setProperty(a.obj, a.name+subfield, val[i]);
  460. onChange(false);
  461. }
  462. };
  463. case "color":
  464. var arr = Std.downcast(current, Array);
  465. var alpha = arr != null && arr.length == 4 || f.attr("alpha") == "true";
  466. var picker = new hide.comp.ColorPicker.ColorBox(null, f, true, alpha);
  467. function updatePicker(val: Dynamic) {
  468. if(arr != null) {
  469. var v = h3d.Vector.fromArray(val);
  470. picker.value = v.toColor();
  471. }
  472. else if(!Math.isNaN(val))
  473. picker.value = val;
  474. }
  475. updatePicker(current);
  476. picker.onChange = function(move) {
  477. if(!move) {
  478. undo(function() {
  479. var f = resolveField();
  480. f.current = getFieldValue();
  481. updatePicker(f.current);
  482. f.onChange(true);
  483. });
  484. }
  485. var newVal : Dynamic =
  486. if(arr != null) {
  487. var vec = h3d.Vector.fromColor(picker.value);
  488. if(alpha)
  489. [vec.x, vec.y, vec.z, vec.w];
  490. else
  491. [vec.x, vec.y, vec.z];
  492. }
  493. else picker.value;
  494. if(!move)
  495. current = newVal;
  496. setFieldValue(newVal);
  497. onChange(false);
  498. };
  499. return;
  500. case "custom":
  501. return;
  502. default:
  503. if( f.is("select") ) {
  504. enumValue = Type.getEnum(current);
  505. if( enumValue != null && f.find("option").length == 0 ) {
  506. var meta = haxe.rtti.Meta.getFields(enumValue);
  507. for( c in enumValue.getConstructors() ) {
  508. var name = c;
  509. var comment = "";
  510. if (Reflect.hasField(meta, c)) {
  511. var fieldMeta = Reflect.getProperty(meta, c);
  512. if (Reflect.hasField(fieldMeta, "display")) {
  513. var displayArr = Reflect.getProperty(fieldMeta, "display");
  514. if (displayArr.length > 0) {
  515. name = displayArr[0];
  516. }
  517. if (displayArr.length > 1) {
  518. comment = displayArr[1];
  519. }
  520. }
  521. }
  522. new Element('<option value="$c" title="$comment">$name</option>').appendTo(f);
  523. }
  524. }
  525. }
  526. if( enumValue != null ) {
  527. var cst = Type.enumConstructor(current);
  528. f.val(cst);
  529. } else
  530. f.val(current);
  531. f.keyup(function(e) {
  532. if( e.keyCode == 13 ) {
  533. f.blur();
  534. return;
  535. }
  536. if( e.keyCode == 27 ) {
  537. f.blur();
  538. return;
  539. }
  540. tempChange = true;
  541. f.change();
  542. });
  543. f.change(function(e) {
  544. var newVal : Dynamic = f.val();
  545. if( f.is("[type=number]") )
  546. newVal = Std.parseFloat(newVal);
  547. if( enumValue != null )
  548. newVal = Type.createEnum(enumValue, newVal);
  549. if( f.is("select") )
  550. f.blur();
  551. setVal(newVal);
  552. });
  553. }
  554. }
  555. function getAccess() : { obj : Dynamic, index : Int, name : String } {
  556. var obj : Dynamic = context;
  557. var path = fname.split(".");
  558. var field = path.pop();
  559. for( p in path ) {
  560. var index = Std.parseInt(p);
  561. if( index != null )
  562. obj = obj[index];
  563. else
  564. obj = Reflect.getProperty(obj, p);
  565. }
  566. var index = Std.parseInt(field);
  567. if( index != null )
  568. return { obj : obj, index : index, name : null };
  569. return { obj : obj, index : -1, name : field };
  570. }
  571. function getAccesses() : Array<{ obj : Dynamic, index : Int, name : String }> {
  572. if (subfields == null)
  573. return [getAccess()];
  574. return [
  575. for (subfield in subfields) {
  576. var a = getAccess();
  577. a.name = a.name + subfield;
  578. a;
  579. }
  580. ];
  581. }
  582. function getFieldValue() {
  583. var a = getAccess();
  584. if( a.name != null )
  585. return Reflect.getProperty(a.obj, a.name);
  586. return a.obj[a.index];
  587. }
  588. function setFieldValue( value : Dynamic ) {
  589. var a = getAccess();
  590. if( a.name != null )
  591. Reflect.setProperty(a.obj, a.name, value);
  592. else
  593. a.obj[a.index] = value;
  594. }
  595. function undo( f : Void -> Void ) {
  596. var a = getAccess();
  597. if( a.name != null )
  598. props.undo.change(Field(a.obj, a.name, current), f);
  599. else
  600. props.undo.change(Array(a.obj, a.index, current), f);
  601. }
  602. function setVal(v) {
  603. if( current == v ) {
  604. // delay history save until last change
  605. if( tempChange || beforeTempChange == null )
  606. return;
  607. current = beforeTempChange.value;
  608. beforeTempChange = null;
  609. }
  610. isTempChange = tempChange;
  611. if( tempChange ) {
  612. tempChange = false;
  613. if( beforeTempChange == null ) beforeTempChange = { value : current };
  614. } else {
  615. undo(function() {
  616. var f = resolveField();
  617. var v = getFieldValue();
  618. f.current = v;
  619. f.element.val(v);
  620. f.element.parent().find("input[type=text]").val(v);
  621. f.onChange(true);
  622. });
  623. }
  624. current = v;
  625. setFieldValue(v);
  626. onChange(false);
  627. }
  628. public dynamic function onChange( wasUndo : Bool ) {
  629. }
  630. function resolveField() {
  631. /*
  632. If our panel has been removed but another bound to the same object has replaced it (a refresh for instance)
  633. let's try to locate the field with same context + name to refresh it instead
  634. */
  635. for( f in viewRoot.find("[field]") ) {
  636. var p : PropsField = Reflect.field(f, "propsField");
  637. if( p != null && p.context == context && p.fname == fname )
  638. return p;
  639. }
  640. return this;
  641. }
  642. }