Add a calculator "skin" UI to your calc project. ScientificCalculator can move to Release 2.0!!

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. package rocks.zipcode.calcskin;
  2. import javafx.application.Application;
  3. import javafx.beans.binding.Bindings;
  4. import javafx.beans.property.*;
  5. import javafx.event.ActionEvent;
  6. import javafx.event.EventHandler;
  7. import javafx.geometry.Pos;
  8. import javafx.scene.Scene;
  9. import javafx.scene.control.*;
  10. import javafx.scene.input.KeyEvent;
  11. import javafx.scene.layout.*;
  12. import javafx.stage.Stage;
  13. import javafx.stage.StageStyle;
  14. import rocks.zipcode.calcskin.CalcEngine;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. /*
  18. - Making a simple 4-function calculator should be trivial for you at this stage.
  19. - BUT, how about adding a column of buttons for x-squared, square-root, inverse (1/x), and exponent (x^y).
  20. - Then you might add a column of buttons that do the Memory functions. (What should you add to the UI to make this easier??)
  21. Finally, work on adding the trig and log functions from the original lab.
  22. Feel free to work in your original groups, get everyone up to speed on this!
  23. */
  24. // a simple JavaFX calculator.
  25. public class CalcSkin extends Application {
  26. public static void main(String[] args){
  27. launch(args);
  28. }
  29. private static final String[][] template = {
  30. { "7", "8", "9", "/" },
  31. { "4", "5", "6", "*" },
  32. { "1", "2", "3", "-" },
  33. { "0", "c", "=", "+" },
  34. { "x^2", "rt", "1/x", "x^y" },
  35. { "cos", "sin", "tan", "log10" },
  36. { "acos","asin", "atan" },
  37. {"store", "use","reset" }
  38. };
  39. private final Map<String, Button> accelerators = new HashMap<>();
  40. private DoubleProperty previousValue = new SimpleDoubleProperty();
  41. private DoubleProperty currentValue = new SimpleDoubleProperty();
  42. private DoubleProperty memoryValue = new SimpleDoubleProperty();
  43. private CalcEngine calcEngine = new CalcEngine();
  44. private enum Op { NOOP, ADD, SUBTRACT, MULTIPLY, DIVIDE, SQUARE, ROOT, INVERSE, EXPO, COS, SIN, TAN, LOG, ACOS, ASIN, ATAN, STORE, USE, RESET }
  45. private Op curOp = Op.NOOP;
  46. private Op stackOp = Op.NOOP;
  47. public static void launchCalc(String... args) {
  48. launch(args);
  49. }
  50. @Override
  51. public void start(Stage stage) {
  52. final TextField screen = createScreen();
  53. final TilePane buttons = createButtons();
  54. stage.setTitle("Calc");
  55. stage.initStyle(StageStyle.UTILITY);
  56. stage.setResizable(false);
  57. stage.setScene(new Scene(createLayout(screen, buttons)));
  58. stage.show();
  59. }
  60. private VBox createLayout(TextField screen, TilePane buttons) {
  61. final VBox layout = new VBox(20);
  62. layout.setAlignment(Pos.CENTER);
  63. layout.setStyle("-fx-background-color: silver; -fx-padding: 20; -fx-font-size: 20;");
  64. layout.getChildren().setAll(screen, buttons);
  65. handleAccelerators(layout);
  66. screen.prefWidthProperty().bind(buttons.widthProperty());
  67. return layout;
  68. }
  69. private void handleAccelerators(VBox layout) {
  70. layout.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
  71. @Override
  72. public void handle(KeyEvent keyEvent) {
  73. Button activated = accelerators.get(keyEvent.getText());
  74. if (activated != null) {
  75. System.out.println("line 94 fired");
  76. activated.fire();
  77. }
  78. }
  79. });
  80. }
  81. private TextField createScreen() {
  82. final TextField screen = new TextField();
  83. screen.setStyle("-fx-background-color: aquamarine;");
  84. screen.setAlignment(Pos.CENTER_RIGHT);
  85. screen.setEditable(false);
  86. screen.textProperty().bind(Bindings.format("%.2f", currentValue));
  87. return screen;
  88. }
  89. private TilePane createButtons() {
  90. TilePane buttons = new TilePane();
  91. buttons.setVgap(7);
  92. buttons.setHgap(7);
  93. buttons.setPrefColumns(template[0].length);
  94. for (String[] r: template) {
  95. for (String s: r) {
  96. buttons.getChildren().add(createButton(s));
  97. }
  98. }
  99. return buttons;
  100. }
  101. private Button createButton(final String s) {
  102. Button button = makeStandardButton(s);
  103. if (s.matches("[0-9]")) {
  104. makeNumericButton(s, button);
  105. } else {
  106. final ObjectProperty<Op> triggerOp = determineOperand(s);
  107. if (triggerOp.get() != Op.NOOP) {
  108. System.out.println(triggerOp.get());
  109. makeOperandButton(button, triggerOp);
  110. } else if ("c".equals(s)) {
  111. makeClearButton(button);
  112. } else if ("=".equals(s)) {
  113. makeEqualsButton(button);
  114. } else if("x^2".equals(s)) {
  115. makeSquareBtn(button);
  116. } else if("rt".equals(s)) {
  117. makeSquareRtBtn(button);
  118. } else if("1/x".equals(s)){
  119. makeInverseBtn(button);
  120. } else if("cos".equals(s)){
  121. makeCOSBtn(button);
  122. } else if("sin".equals(s)){
  123. makeSINBtn(button);
  124. } else if("tan".equals(s)){
  125. makeTANBtn(button);
  126. } else if("log10".equals(s)){
  127. makeLOGBtn(button);
  128. } else if("acos".equals(s)){
  129. makeCOSBtn(button);
  130. } else if("asin".equals(s)){
  131. makeSINBtn(button);
  132. } else if("atan".equals(s)){
  133. makeTANBtn(button);
  134. }else if("store".equals(s)){
  135. makeStoreBtn(button);
  136. } else if("use".equals(s)){
  137. makeUseBtn(button);
  138. } else if("reset".equals(s)) {
  139. makeResetBtn(button);
  140. }
  141. }
  142. return button;
  143. }
  144. private ObjectProperty<Op> determineOperand(String s) {
  145. final ObjectProperty<Op> triggerOp = new SimpleObjectProperty<>(Op.NOOP);
  146. switch (s) {
  147. case "+": triggerOp.set(Op.ADD); break;
  148. case "-": triggerOp.set(Op.SUBTRACT); break;
  149. case "*": triggerOp.set(Op.MULTIPLY); break;
  150. case "/": triggerOp.set(Op.DIVIDE); break;
  151. case "x^y" : triggerOp.set(Op.EXPO); break;
  152. }
  153. return triggerOp;
  154. }
  155. private void makeOperandButton(Button button, final ObjectProperty<Op> triggerOp) {
  156. button.setStyle("-fx-base: lightgray;");
  157. button.setOnAction(new EventHandler<ActionEvent>() {
  158. @Override
  159. public void handle(ActionEvent actionEvent) {
  160. System.out.println("Fire line: 169");
  161. curOp = triggerOp.get();
  162. System.out.println(curOp);
  163. }
  164. });
  165. }
  166. private Button makeStandardButton(String s) {
  167. Button button = new Button(s);
  168. button.setStyle("-fx-base: beige;");
  169. accelerators.put(s, button);
  170. button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
  171. return button;
  172. }
  173. private void makeNumericButton(final String s, Button button) {
  174. button.setOnAction(new EventHandler<ActionEvent>() {
  175. @Override
  176. public void handle(ActionEvent actionEvent) {
  177. if (curOp == Op.NOOP) {
  178. currentValue.set(currentValue.get() * 10 + Integer.parseInt(s));
  179. } else {
  180. previousValue.set(currentValue.get());
  181. currentValue.set(Integer.parseInt(s));
  182. stackOp = curOp;
  183. curOp = Op.NOOP;
  184. }
  185. }
  186. });
  187. }
  188. private void makeClearButton(Button button) {
  189. button.setStyle("-fx-base: mistyrose;");
  190. button.setOnAction(new EventHandler<ActionEvent>() {
  191. @Override
  192. public void handle(ActionEvent actionEvent) {
  193. currentValue.set(0);
  194. }
  195. });
  196. }
  197. private void makeEqualsButton(Button button) {
  198. button.setStyle("-fx-base: ghostwhite;");
  199. button.setOnAction(new EventHandler<ActionEvent>() {
  200. @Override
  201. public void handle(ActionEvent actionEvent) {
  202. System.out.println("Fire line 221");
  203. switch (stackOp) {
  204. case ADD: currentValue.set(calcEngine.add(previousValue.get(), currentValue.get())); break;
  205. case SUBTRACT: currentValue.set(calcEngine.subtract(previousValue.get(), currentValue.get())); break;
  206. case MULTIPLY: currentValue.set(calcEngine.multiply(previousValue.get(), currentValue.get())); break;
  207. case DIVIDE: currentValue.set(calcEngine.divide(previousValue.get(), currentValue.get())); break;
  208. case EXPO: currentValue.set(calcEngine.expo(previousValue.get(), currentValue.get())); break;
  209. }
  210. }
  211. });
  212. }
  213. //CREATE THE SQUARED FUNCTION FOR THE X^2 BUTTON
  214. private void makeSquareBtn(Button button) {
  215. button.setStyle("-fx-base: lightgray;");
  216. button.setOnAction(new EventHandler<ActionEvent>() {
  217. @Override
  218. public void handle(ActionEvent actionEvent) {
  219. currentValue.set(calcEngine.square(currentValue.get()));
  220. stackOp = Op.SQUARE;
  221. }
  222. });
  223. }
  224. //CREATE THE SQUARED ROOT FUNCTION FOR THE RT BUTTON
  225. private void makeSquareRtBtn(Button button) {
  226. button.setStyle("-fx-base: lightgray;");
  227. button.setOnAction(new EventHandler<ActionEvent>() {
  228. @Override
  229. public void handle(ActionEvent actionEvent) {
  230. currentValue.set(calcEngine.root(currentValue.get()));
  231. stackOp = Op.ROOT;
  232. }
  233. });
  234. }
  235. //CREATE THE INVERSE FUNCTION FOR THE INV BUTTON
  236. private void makeInverseBtn(Button button) {
  237. button.setStyle("-fx-base: lightgray;");
  238. button.setOnAction(new EventHandler<ActionEvent>() {
  239. @Override
  240. public void handle(ActionEvent actionEvent) {
  241. currentValue.set(calcEngine.inverse(currentValue.get()));
  242. stackOp = Op.INVERSE;
  243. }
  244. });
  245. }
  246. //CREATE THE COS FUNCTION FOR THE COS BUTTON
  247. private void makeCOSBtn(Button button) {
  248. button.setStyle("-fx-base: lightgray;");
  249. button.setOnAction(new EventHandler<ActionEvent>() {
  250. @Override
  251. public void handle(ActionEvent actionEvent) {
  252. currentValue.set(calcEngine.cos(currentValue.get()));
  253. stackOp = Op.COS;
  254. }
  255. });
  256. }
  257. //CREATE THE SIN FUNCTION FOR THE SIN BUTTON
  258. private void makeSINBtn(Button button) {
  259. button.setStyle("-fx-base: lightgray;");
  260. button.setOnAction(new EventHandler<ActionEvent>() {
  261. @Override
  262. public void handle(ActionEvent actionEvent) {
  263. currentValue.set(calcEngine.sin(currentValue.get()));
  264. stackOp = Op.SIN;
  265. }
  266. });
  267. }
  268. //CREATE THE TAN FUNCTION FOR THE TAN BUTTON
  269. private void makeTANBtn(Button button) {
  270. button.setStyle("-fx-base: lightgray;");
  271. button.setOnAction(new EventHandler<ActionEvent>() {
  272. @Override
  273. public void handle(ActionEvent actionEvent) {
  274. currentValue.set(calcEngine.tan(currentValue.get()));
  275. stackOp = Op.TAN;
  276. }
  277. });
  278. }
  279. //CREATE THE ACOS FUNCTION FOR THE ACOS BUTTON
  280. private void makeACOSBtn(Button button) {
  281. button.setStyle("-fx-base: lightgray;");
  282. button.setOnAction(new EventHandler<ActionEvent>() {
  283. @Override
  284. public void handle(ActionEvent actionEvent) {
  285. currentValue.set(calcEngine.acos(currentValue.get()));
  286. stackOp = Op.ACOS;
  287. }
  288. });
  289. }
  290. //CREATE THE ASIN FUNCTION FOR THE ASIN BUTTON
  291. private void makeASINBtn(Button button) {
  292. button.setStyle("-fx-base: lightgray;");
  293. button.setOnAction(new EventHandler<ActionEvent>() {
  294. @Override
  295. public void handle(ActionEvent actionEvent) {
  296. currentValue.set(calcEngine.asin(currentValue.get()));
  297. stackOp = Op.ASIN;
  298. }
  299. });
  300. }
  301. //CREATE THE ATAN FUNCTION FOR THE ATAN BUTTON
  302. private void makeATANBtn(Button button) {
  303. button.setStyle("-fx-base: lightgray;");
  304. button.setOnAction(new EventHandler<ActionEvent>() {
  305. @Override
  306. public void handle(ActionEvent actionEvent) {
  307. currentValue.set(calcEngine.atan(currentValue.get()));
  308. stackOp = Op.ATAN;
  309. }
  310. });
  311. }
  312. //CREATE THE COS FUNCTION FOR THE COS BUTTON
  313. private void makeLOGBtn(Button button) {
  314. button.setStyle("-fx-base: lightgray;");
  315. button.setOnAction(new EventHandler<ActionEvent>() {
  316. @Override
  317. public void handle(ActionEvent actionEvent) {
  318. currentValue.set(calcEngine.log(currentValue.get()));
  319. stackOp = Op.LOG;
  320. }
  321. });
  322. }
  323. //CREATE THE STORE FUNCTION FOR THE STORE BUTTON
  324. private void makeStoreBtn(Button button) {
  325. button.setStyle("-fx-base: lightgray;");
  326. button.setOnAction(new EventHandler<ActionEvent>() {
  327. @Override
  328. public void handle(ActionEvent actionEvent) {
  329. memoryValue.set(currentValue.get());
  330. stackOp = Op.STORE;
  331. }
  332. });
  333. }
  334. //CREATE THE USE FUNCTION FOR THE USE BUTTON
  335. private void makeUseBtn(Button button) {
  336. button.setStyle("-fx-base: lightgray;");
  337. button.setOnAction(new EventHandler<ActionEvent>() {
  338. @Override
  339. public void handle(ActionEvent actionEvent) {
  340. if(curOp == Op.NOOP){
  341. currentValue.set(currentValue.get() * 10 + memoryValue.get());
  342. } else {
  343. previousValue.set(currentValue.get());
  344. currentValue.set(memoryValue.get());
  345. stackOp = Op.USE;
  346. stackOp = curOp;
  347. curOp = Op.NOOP;
  348. }
  349. }
  350. });
  351. }
  352. //CREATE THE COS FUNCTION FOR THE COS BUTTON
  353. private void makeResetBtn(Button button) {
  354. button.setStyle("-fx-base: lightgray;");
  355. button.setOnAction(new EventHandler<ActionEvent>() {
  356. @Override
  357. public void handle(ActionEvent actionEvent) {
  358. memoryValue.set(0);
  359. stackOp = Op.RESET;
  360. }
  361. });
  362. }
  363. }