ZenScript main repository
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DInputField.java 8.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * To change this license header, choose License Headers in Project Properties.
  3. * To change this template file, choose Tools | Templates
  4. * and open the template in the editor.
  5. */
  6. package org.openzen.drawablegui;
  7. import org.openzen.drawablegui.draw.DDrawSurface;
  8. import org.openzen.drawablegui.draw.DDrawnRectangle;
  9. import org.openzen.drawablegui.draw.DDrawnShape;
  10. import org.openzen.drawablegui.draw.DDrawnText;
  11. import org.openzen.drawablegui.listeners.ListenerHandle;
  12. import org.openzen.drawablegui.live.LiveObject;
  13. import org.openzen.drawablegui.live.LiveString;
  14. import org.openzen.drawablegui.live.MutableLiveObject;
  15. import org.openzen.drawablegui.live.MutableLiveString;
  16. import org.openzen.drawablegui.style.DDimension;
  17. import org.openzen.drawablegui.style.DStyleClass;
  18. import org.openzen.drawablegui.style.DStylePath;
  19. /**
  20. *
  21. * @author Hoofdgebruiker
  22. */
  23. public class DInputField implements DComponent {
  24. public final MutableLiveString value;
  25. private final ListenerHandle<LiveString.Listener> valueListener;
  26. private final DStyleClass styleClass;
  27. private final MutableLiveObject<DSizing> sizing = DSizing.create();
  28. private DIRectangle bounds = DIRectangle.EMPTY;
  29. private final DDimension preferredWidth;
  30. private DDrawSurface surface;
  31. private int z;
  32. private DInputFieldStyle style;
  33. private DFontMetrics fontMetrics;
  34. private int cursorFrom = -1;
  35. private int cursorTo = -1;
  36. private Runnable onEnter = null;
  37. private Runnable onEscape = null;
  38. private boolean cursorBlink = true;
  39. private DTimerHandle blinkTimer;
  40. private DDrawnShape shape;
  41. private DDrawnText text;
  42. private DDrawnRectangle cursor;
  43. private DDrawnRectangle selection;
  44. public DInputField(DStyleClass styleClass, MutableLiveString value, DDimension preferredWidth) {
  45. this.styleClass = styleClass;
  46. this.value = value;
  47. this.preferredWidth = preferredWidth;
  48. valueListener = value.addListener((oldValue, newValue) -> handleValueUpdated(newValue));
  49. cursorFrom = 0;
  50. cursorTo = value.getValue().length();
  51. }
  52. public void setOnEnter(Runnable onEnter) {
  53. this.onEnter = onEnter;
  54. }
  55. public void setOnEscape(Runnable onEscape) {
  56. this.onEscape = onEscape;
  57. }
  58. @Override
  59. public void close() {
  60. valueListener.close();
  61. unmount();
  62. }
  63. @Override
  64. public void mount(DStylePath parent, int z, DDrawSurface surface) {
  65. this.surface = surface;
  66. this.z = z;
  67. DStylePath path = parent.getChild("input", styleClass);
  68. style = new DInputFieldStyle(surface.getStylesheet(path));
  69. fontMetrics = surface.getFontMetrics(style.font);
  70. sizing.setValue(new DSizing(
  71. preferredWidth.evalInt(surface.getContext()) + style.margin.getHorizontal() + style.border.getPaddingHorizontal(),
  72. fontMetrics.getAscent() + fontMetrics.getDescent() + style.margin.getVertical() + style.border.getPaddingVertical()));
  73. if (blinkTimer != null)
  74. blinkTimer.close();
  75. blinkTimer = surface.getContext().setTimer(300, this::blink);
  76. if (text != null)
  77. text.close();
  78. text = surface.drawText(
  79. z + 2,
  80. style.font,
  81. style.color,
  82. bounds.x + style.margin.left + style.border.getPaddingLeft(),
  83. bounds.y + style.margin.top + style.border.getPaddingTop() + fontMetrics.getAscent(),
  84. value.getValue());
  85. if (cursor != null)
  86. cursor.close();
  87. cursor = surface.fillRect(z + 2, DIRectangle.EMPTY, cursorBlink ? style.cursorColor : 0);
  88. if (selection != null)
  89. selection.close();
  90. selection = surface.fillRect(z + 1, DIRectangle.EMPTY, 0);
  91. setCursor(cursorFrom, cursorTo);
  92. }
  93. @Override
  94. public void unmount() {
  95. blinkTimer.close();
  96. if (style != null)
  97. style.border.close();
  98. if (shape != null)
  99. shape.close();
  100. if (text != null)
  101. text.close();
  102. if (cursor != null)
  103. cursor.close();
  104. if (selection != null)
  105. selection.close();
  106. }
  107. private void blink() {
  108. cursorBlink = !cursorBlink;
  109. cursor.setColor(cursorBlink ? style.cursorColor : 0);
  110. }
  111. @Override
  112. public LiveObject<DSizing> getSizing() {
  113. return sizing;
  114. }
  115. @Override
  116. public DIRectangle getBounds() {
  117. return bounds;
  118. }
  119. @Override
  120. public int getBaselineY() {
  121. return style.margin.top + style.border.getPaddingTop() + fontMetrics.getAscent();
  122. }
  123. @Override
  124. public void setBounds(DIRectangle bounds) {
  125. this.bounds = bounds;
  126. setCursor(cursorFrom, cursorTo);
  127. if (shape != null)
  128. shape.close();
  129. shape = surface.fillPath(z, style.shape.instance(style.margin.apply(bounds)), DTransform2D.IDENTITY, style.backgroundColor);
  130. text.setPosition(
  131. bounds.x + style.margin.left + style.border.getPaddingLeft(),
  132. bounds.y + style.margin.top + style.border.getPaddingTop() + fontMetrics.getAscent());
  133. style.border.update(surface, z, bounds);
  134. }
  135. @Override
  136. public void onMouseEnter(DMouseEvent e) {
  137. surface.getContext().setCursor(DUIContext.Cursor.TEXT);
  138. }
  139. @Override
  140. public void onMouseExit(DMouseEvent e) {
  141. surface.getContext().setCursor(DUIContext.Cursor.NORMAL);
  142. }
  143. @Override
  144. public void onMouseClick(DMouseEvent e) {
  145. surface.getContext().getWindow().focus(this);
  146. }
  147. @Override
  148. public void onKeyPressed(DKeyEvent e) {
  149. boolean shift = e.has(DKeyEvent.SHIFT);
  150. switch (e.keyCode) {
  151. case UP:
  152. setCursor(0, 0);
  153. break;
  154. case DOWN:
  155. setCursor(value.getValue().length(), value.getValue().length());
  156. break;
  157. case LEFT: {
  158. int to = Math.max(0, cursorTo - 1);
  159. setCursor(shift ? cursorFrom : to, to);
  160. break;
  161. }
  162. case RIGHT: {
  163. int to = Math.min(value.getValue().length(), cursorTo + 1);
  164. setCursor(shift ? cursorFrom : to, to);
  165. break;
  166. }
  167. case DELETE:
  168. delete();
  169. break;
  170. case BACKSPACE:
  171. backspace();
  172. break;
  173. case ENTER:
  174. enter();
  175. break;
  176. case ESCAPE:
  177. escape();
  178. break;
  179. default:
  180. if (e.character == DKeyEvent.CHAR_UNDEFINED)
  181. return;
  182. insert(Character.toString(e.character));
  183. break;
  184. }
  185. }
  186. private void setCursor(int from, int to) {
  187. cursorFrom = from;
  188. cursorTo = to;
  189. int cursorXFrom = fontMetrics.getWidth(value.getValue(), 0, Math.min(cursorFrom, cursorTo));
  190. int cursorXTo = fontMetrics.getWidth(value.getValue(), 0, Math.max(cursorFrom, cursorTo));
  191. if (cursorFrom != cursorTo) {
  192. selection.setRectangle(new DIRectangle(
  193. bounds.x + style.margin.left + style.border.getPaddingLeft() + cursorXFrom,
  194. bounds.y + style.margin.top + style.border.getPaddingTop(),
  195. cursorXTo - cursorXFrom,
  196. fontMetrics.getAscent() + fontMetrics.getDescent()));
  197. selection.setColor(style.selectionColor);
  198. } else {
  199. selection.setColor(0);
  200. }
  201. cursor.setRectangle(new DIRectangle(
  202. bounds.x + style.margin.left + style.border.getPaddingLeft() + cursorXTo,
  203. bounds.y + style.margin.top + style.border.getPaddingTop(),
  204. style.cursorWidth,
  205. fontMetrics.getAscent() + fontMetrics.getDescent()));
  206. }
  207. private void handleValueUpdated(String newValue) {
  208. if (text != null)
  209. text.close();
  210. text = surface.drawText(
  211. z + 2,
  212. style.font,
  213. style.color,
  214. bounds.x + style.margin.left + style.border.getPaddingLeft(),
  215. bounds.y + style.margin.top + style.border.getPaddingTop() + fontMetrics.getAscent(),
  216. value.getValue());
  217. }
  218. private void backspace() {
  219. if (cursorFrom == 0 && cursorTo == 0)
  220. return;
  221. if (cursorFrom == cursorTo) {
  222. value.setValue(value.getValue().substring(0, cursorFrom - 1) + value.getValue().substring(cursorFrom));
  223. setCursor(cursorFrom - 1, cursorTo - 1);
  224. } else {
  225. int from = Math.min(cursorFrom, cursorTo);
  226. int to = Math.max(cursorFrom, cursorTo);
  227. setCursor(from, from);
  228. value.setValue(value.getValue().substring(0, from) + value.getValue().substring(to));
  229. }
  230. }
  231. private void delete() {
  232. if (cursorFrom == 0 && cursorTo == 0)
  233. return;
  234. if (cursorFrom == cursorTo) {
  235. if (cursorFrom < value.getValue().length()) {
  236. value.setValue(value.getValue().substring(0, cursorFrom) + value.getValue().substring(cursorFrom + 1));
  237. }
  238. } else {
  239. int from = Math.min(cursorFrom, cursorTo);
  240. int to = Math.max(cursorFrom, cursorTo);
  241. setCursor(from, from);
  242. value.setValue(value.getValue().substring(0, from) + value.getValue().substring(to));
  243. }
  244. }
  245. private void insert(String value) {
  246. int from = Math.min(cursorFrom, cursorTo);
  247. int to = Math.max(cursorFrom, cursorTo);
  248. this.value.setValue(this.value.getValue().substring(0, from) + value + this.value.getValue().substring(to));
  249. setCursor(from + value.length(), from + value.length());
  250. }
  251. private void enter() {
  252. if (onEnter != null)
  253. onEnter.run();
  254. }
  255. private void escape() {
  256. if (onEscape != null)
  257. onEscape.run();
  258. }
  259. }