MultiRange.hx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package hide.comp;
  2. class MultiRange extends Component {
  3. public var value(get, set) : Array<Float>;
  4. var current : Array<Float> = [];
  5. var numValues : Int = 0;
  6. var ranges : Array<Range> = [];
  7. var isUniform : Bool = false;
  8. var linkButton : Element;
  9. var linkIcon : Element;
  10. static final uniformLockKey = "multiRangeUniformLock";
  11. function set_value(v : Array<Float>) {
  12. if (v.length != numValues)
  13. throw "assert";
  14. current = v;
  15. repaint();
  16. return v;
  17. }
  18. function get_value() {
  19. return current;
  20. }
  21. function repaint(?ignoreIndex : Int) {
  22. for (i => range in ranges) {
  23. if (ignoreIndex == null || ignoreIndex != i)
  24. range.value = current[i];
  25. }
  26. syncRanges();
  27. linkButton.toggleClass("toggled", isUniform);
  28. linkIcon.toggleClass("ico-link", isUniform);
  29. linkIcon.toggleClass("ico-unlink", !isUniform);
  30. }
  31. function syncRanges() {
  32. var biggestMin = Math.NEGATIVE_INFINITY;
  33. var smallestMax = Math.POSITIVE_INFINITY;
  34. for (range in ranges) {
  35. biggestMin = Math.max(biggestMin, @:privateAccess range.curMin);
  36. smallestMax = Math.min(smallestMax, @:privateAccess range.curMax);
  37. }
  38. for (range in ranges) {
  39. range.element.attr("min", biggestMin);
  40. range.element.attr("max", smallestMax);
  41. }
  42. }
  43. public dynamic function onChange(tempChange : Bool) {
  44. }
  45. function change(tempChange : Bool) {
  46. onChange(tempChange);
  47. }
  48. public function new(?parent:Element,?root:Element, num : Int , labels : Array<String>) {
  49. this.numValues = num;
  50. super(null, null);
  51. saveDisplayKey = "MultiRange";
  52. var flex = new Element("<div>").css("position", "relative").css("width", "110%").appendTo(parent);
  53. flex.css("display", "flex");
  54. var ds = getDisplayState(uniformLockKey + ":" + labels[0]);
  55. if (ds != null) {
  56. isUniform = ds == true;
  57. }
  58. var rows = new Element("<div>").css("flex", "1 0").appendTo(flex);
  59. var min = root.attr("min");
  60. if (min == null) min = "0";
  61. var max = root.attr("max");
  62. if (max == null) max = "5";
  63. for (i in 0...numValues) {
  64. var row = new Element("<div>").appendTo(rows);
  65. var dt = new Element("<dt>").text(labels[i]).appendTo(row);
  66. var dd = new Element("<dd>").appendTo(row);
  67. var range = new Range(dd, new Element('<input type="range" min="$min" max="$max" tabindex="-1">'));
  68. PropsEditor.wrapDt(dt, "1", function(e) {
  69. if (e.ctrlKey) {
  70. range.value = Math.round(range.value);
  71. range.onChange(false);
  72. } else {
  73. range.value = 1;
  74. range.onChange(false);
  75. }
  76. });
  77. range.onChange = function(tempChange:Bool) {
  78. if (isUniform) {
  79. var scale = range.value / current[i];
  80. for (j in 0...numValues) {
  81. if (!Math.isFinite(scale)) {
  82. current[j] = range.value;
  83. }
  84. else {
  85. current[j] = Math.fround(current[j] * scale * 100.0) / 100.0;
  86. }
  87. }
  88. repaint(i);
  89. }
  90. else {
  91. current[i] = range.value;
  92. }
  93. onChange(tempChange);
  94. };
  95. ranges.push(range);
  96. }
  97. var linkContainer = new Element("<div class='link-container'>").css("flex-shirnk", "1").css("left","-32px").css("position", "relative").appendTo(flex);
  98. linkContainer.append(new Element("<div class='link link-up'>"));
  99. linkButton = new Element("<div class='hide-button' title='Link/Unlink sliders. Right click to open the context menu'>").appendTo(linkContainer);
  100. linkIcon = new Element("<div class='icon ico ico-link'>").appendTo(linkButton);
  101. linkContainer.append(new Element("<div class='link link-down'>"));
  102. linkButton.click(function(e) {
  103. isUniform = !isUniform;
  104. saveDisplayState(uniformLockKey + ":" + labels[0], isUniform);
  105. repaint();
  106. });
  107. linkButton.contextmenu(function(e) {
  108. e.preventDefault();
  109. new ContextMenu([
  110. { label : "Reset All", click : reset },
  111. { label : "Round All", click : round },
  112. { label : "sep", isSeparator: true},
  113. { label : "Copy", click : copy},
  114. { label : "Paste", click: paste, enabled : canPaste()},
  115. { label : "sep", isSeparator: true},
  116. { label : "Cancel", click : function() {} },
  117. ]);
  118. return false;
  119. });
  120. root.remove();
  121. repaint();
  122. }
  123. function round() {
  124. for (i => v in current) {
  125. current[i] = Math.fround(v);
  126. }
  127. repaint();
  128. change(false);
  129. }
  130. function reset() {
  131. value = [for (i in 0...numValues) 1.0];
  132. change(false);
  133. }
  134. function copy() {
  135. ide.setClipboard(current.join(","));
  136. }
  137. function paste() {
  138. var vals = getPasteValues();
  139. if (vals != null) {
  140. value = vals;
  141. change(false);
  142. }
  143. }
  144. function getPasteValues() : Null<Array<Float>> {
  145. var arr = ide.getClipboard().split(",");
  146. if (arr.length == numValues) {
  147. var arrFloat = arr.map((s) -> Std.parseFloat(s));
  148. if (!arrFloat.contains(Math.NaN))
  149. return arrFloat;
  150. }
  151. return null;
  152. }
  153. function canPaste() {
  154. return getPasteValues() != null;
  155. }
  156. }