JavaFX - طريقة إنشاء آلة حاسبة علمية

JavaFX طريقة إنشاء آلة حاسبة علمية

في هذا الدرس ستتعلم طريقة إنشاء آلة حاسبة علمية ( Scientific Calculator ) باستخدام JavaFX.

In this lesson, you will learn how to create a Scientific Calculator using JavaFX.

اله حاسبه علمية جافا fx


بناء برنامج آلة حاسبة علمية

البرنامج يحتوي على ملفين فقط, ملف جافا و ملف التصميم.

  • ملف الجافا وضعناه مباشرةً في المشروع.

  • ملف التصميم وضعناه بداخل مجلد إسمه css.


خيارات التحميل

⇓ تحميل البرنامج ⇓ تحميل المشروع كاملاً


كود البرنامج  آلة حاسبة علمية

style.css
/*
   في هذا الملف وضعنا كل كود التصميم الذي يحدد لون الخط + حجمه + نوع الخط + لون خلفية
   الذي قمنا بإعطائه لكل عنصر وضعناه في النافذة Id كل شيء أضفناه في النافذة عن طريق إسم الـ
*/
 
#root {
    -fx-background-color: #222;
}

#b0, #b1, #b2, #b3, #b4, #b5, #b6, #b7, #b8, #b9, #comma {
    -fx-text-fill: white;
    -fx-background-color: dimgray;
}

#plus, #minus, #multiple, #divide, #equal, #cos, #sin, #tan, #sqrt,
#pi, #exponential, #modulo, #parentesesLeft, #parentesesRight, #power {
    -fx-text-fill: white;
    -fx-background-color: darkslategray;
}

#b0, #b1, #b2, #b3, #b4, #b5, #b6, #b7, #b8, #b9, #comma ,#plus, #minus,
#multiple, #divide, #equal, #sqrt, #pi, #exponential, #modulo, #parentesesLeft,
#parentesesRight, #power {
    -fx-font-family: Arial;
    -fx-font-size: 17px;
    -fx-font-weight: bold;
}

#cos, #sin, #tan {
    -fx-font-family: Arial;
    -fx-font-size: 14px;
    -fx-font-weight: bold;
}

#clear {
    -fx-text-fill: white;
    -fx-background-color: #E50101;
}

#back {
    -fx-text-fill: white;
    -fx-background-color: orangered;
}

#textField {
    -fx-background-color: lightgoldenrodyellow;
    -fx-font-family: Monospaced;
    -fx-font-size: 19px;
    -fx-font-weight: bold;
}

#historyText {
    -fx-font-family: Arial;
    -fx-font-size: 16px;
    -fx-font-weight: bold;
}

#historyText .content {
    -fx-background-color: #dcdcdc;
}

.menu-bar {
    -fx-background-color: #333;
}

.menu-bar .label {
    -fx-text-fill: white;
    -fx-font-weight: bold;
}

.context-menu {
  -fx-background-color: #333;
}
		

Main.java
import java.text.DecimalFormat;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SeparatorMenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class Main extends Application {

	// الذي سيمثل نافذة البرنامج نفسه لكي نستطيع الوصول إليه من داخل أي دالة نقوم بإنشائها في البرنامج Stage قمنا بتعريف كائن الـ
    Stage myStage;

    // هنا قمنا بإنشاء جميع الأزرار و مربعات النصوص التي سنضعها في النافذة
    Pane root = new Pane();
    Button b0 = new Button("0");
    Button b1 = new Button("1");
    Button b2 = new Button("2");
    Button b3 = new Button("3");
    Button b4 = new Button("4");
    Button b5 = new Button("5");
    Button b6 = new Button("6");
    Button b7 = new Button("7");
    Button b8 = new Button("8");
    Button b9 = new Button("9");
    Button comma = new Button(".");
    Button plus = new Button("+");
    Button minus = new Button("-");
    Button multiple = new Button("×");
    Button divide = new Button("÷");
    Button cos = new Button("cos");
    Button sin = new Button("sin");
    Button tan = new Button("tan");
    Button sqrt = new Button("√");
    Button power = new Button("^");
    Button modulo = new Button("%");
    Button exponential = new Button("e");
    Button pi = new Button("π");
    Button parentesesLeft = new Button("(");
    Button parentesesRight = new Button(")");
    Button equal = new Button("=");
    Button clear = new Button("C");
    Button back = new Button("←");
    TextField textField = new TextField("");
    TextArea historyText = new TextArea();

    // هنا قمنا بإنشاء شريط القوائم, القوائم و العناصر التي بداخلهم
    MenuBar menuBar = new MenuBar();
    Menu view = new Menu(" View ");
    Menu edit = new Menu(" Edit ");
    Menu help = new Menu(" Help ");
    CheckMenuItem history = new CheckMenuItem("History");
    MenuItem copy = new MenuItem("Copy");
    MenuItem paste = new MenuItem("Paste");
    MenuItem copyHistory = new MenuItem("Copy History");
    MenuItem clearHistory = new MenuItem("Clear History");
    MenuItem keyboardShortcuts = new MenuItem("Keyboard Shortcuts");
    MenuItem about = new MenuItem("About");

    // لإخفاء الأصفار التي لا حاجة إلى ظهورها format سنستخدم الكائن
    DecimalFormat format = new DecimalFormat("0.###############");

	// لعرض النوافذ المنبثقة التي ستظهر عندما يقوم المستخدم بالنقر على الخيارات التي وضعناها في شريط القوائم alert سنستخدم الكائن
    Alert alert = new Alert(AlertType.INFORMATION);

	
    // سيتم إستدعاء هذه الدالة في كل مرة قبل إدخال أي رقم, حرف أو رمز لتعديل نص المعادلة بشكل تلقائي عند الحاجة
    // و هي تقوم بمقارنة آخر حرف مدخل في مربع النص مع الشيء الذي سيقوم المستخدم بإدخاله
    private void autoAddOrRemove(String button) {

        // lastCharacter إذا لم يكن مربع النص فارغاً سيتم تخزين آخر رقم, رمز أو حرف ظاهر في مربع النص في الكائن
        if (!textField.getText().isEmpty()) {
            Character lastCharacter = textField.getText().charAt(textField.getText().length() - 1);

            switch (button) {

                // هو أحد الإحتمالات التالية, سيتم إضافة ×, ×0 أو ) بعدهlastCharacter إذا تم النقر على رمز و كان الشيء المخزن في الكائن
                case "symbol":
                    switch (lastCharacter) {
                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                        case 'e':
                        case 'π':
                            textField.setText(textField.getText() + "×");
                            break;
                        case '.':
                            textField.setText(textField.getText() + "0×");
                            break;
                    }
                    break;

                // سيتم إضافة الرمز × بعده ,π أو e هو lastCharacter إذا تم النقر على رقم و كان الشيء المخزن في الكائن
                // عبارة عن 0 لا حاجة له سيتم مسحه فقط lastCharacter و إذا تم النقر على رقم و كان آخر رقم مخزن في الكائن
                case "number":
                    switch (lastCharacter) {
                        case 'e':
                        case 'π':
                            textField.setText(textField.getText() + "×");
                            break;
                        case '0':
                            switch (textField.getText()) {
                                case "0":
                                case "+0":
                                case "-0":
                                case "×0":
                                case "÷0":
                                case "%0":
                                case "^0":
                                case "√0":
                                case "(0":
                                case "cos0":
                                case "sin0":
                                case "tan0":
                                    textField.setText(textField.getText().substring(0, textField.getText().length() - 1));
                                    break;
                            }
                            break;
                    }
                    break;

                // هو أحد العوامل التالية أو نقطة سيتم مسحه lastCharacter إذا تم النقر على عامل و كان الشيء المخزن في الكائن
                // بهذه الطريقة لن يستطيع المستخدم وضع أكثر من عاملين وراء بعض أو وضع عامل مباشرةً بعد نقطة
                case "operand":
                    switch (lastCharacter) {
                        case '+':
                        case '-':
                        case '×':
                        case '÷':
                        case '%':
                        case '.':
                            textField.setText(textField.getText().substring(0, textField.getText().length() - 1));
                            break;
                    }
                    break;

                // هو أحد الإحتمالات التالية lastCharacter إذا تم النقر على النقطة و كان الشيء المخزن في الكائن
                // سيتم إضافة 0 أو 0× بعدها, أو مسح النقطة لضمان عدم وجود نقطتين وراء بعض
                case "point":
                    switch (lastCharacter) {
                        case '+':
                        case '-':
                        case '×':
                        case '÷':
                        case '%':
                        case '(':
                        case '√':
                        case 'π':
                        case 's':
                        case 'n':
                        case '^':
                            textField.setText(textField.getText() + "0");
                            break;
                        case ')':
                            textField.setText(textField.getText() + "×0");
                            break;
                        case '.':
                            textField.setText(textField.getText().substring(0, textField.getText().length() - 1));
                            break;
                    }
                    break;

            }
        }
    }

    // double هذه الدالة ترجع ناتج المعادلة المدخلة في مربع النص كـ
    // فعلياً تقوم بتجزئة المعادلة على حسب العوامل و الرموز الموجودة فيها و كل جزئين تم إكتشاف قيمتهما يتحولان إلى جزء واحد
    // و هي تستمر على هذه الحال حتى تصبح كل الأجزاء تساوي رقماً واحداً, و عندها تقوم بإرجاعه
    private double calculate(String str) {

        return new Object() {
            int pos = -1, ch;

            void nextChar() {
                ch = (++pos < str.length()) ? str.charAt(pos) : -1;
            }

            boolean eat(int charToEat) {
                while (ch == ' ') {
                    nextChar();
                }
                if (ch == charToEat) {
                    nextChar();
                    return true;
                }
                return false;
            }

            double parse() {
                nextChar();
                double x = parseExpression();
                if (pos < str.length()) {
                    throw new RuntimeException("Unexpected: " + (char) ch);
                }
                return x;
            }

            double parseExpression() {
                double x = parseTerm();
                for (;;) {
                    if (eat('+')) {
                        x += parseTerm();
                    } else if (eat('-')) {
                        x -= parseTerm();
                    } else {
                        return x;
                    }
                }
            }

            double parseTerm() {
                double x = parseFactor();
                for (;;) {
                    if (eat('×')) {
                        x *= parseFactor();
                    } else if (eat('÷')) {
                        x /= parseFactor();
                    } else if (eat('%')) {
                        x %= parseFactor();
                    } else {
                        return x;
                    }
                }
            }

            double parseFactor() {
                if (eat('+')) {
                    return parseFactor();
                }
                if (eat('-')) {
                    return -parseFactor();
                }
                double x;
                int startPos = this.pos;
                if (eat('(')) {
                    x = parseExpression();
                    eat(')');
                } else if (eat('e')) {
                    x = Math.E;
                } else if (eat('π')) {
                    x = Math.PI;
                } else if ((ch >= '0' && ch <= '9') || ch == '.') {
                    while ((ch >= '0' && ch <= '9') || ch == '.') {
                        nextChar();
                    }
                    x = Double.parseDouble(str.substring(startPos, this.pos));
                } else if (ch >= 'a' && ch <= 'z' || ch == '√') {
                    while (ch >= 'a' && ch <= 'z' || ch == '√') {
                        nextChar();
                    }
                    String func = str.substring(startPos, this.pos);
                    x = parseFactor();
                    switch (func) {
                        case "√":
                            x = Math.sqrt(x);
                            break;
                        case "sin":
                            x = Math.sin(Math.toRadians(x));
                            break;
                        case "cos":
                            x = Math.cos(Math.toRadians(x));
                            break;
                        case "tan":
                            x = Math.tan(Math.toRadians(x));
                            break;
                        default:
                            throw new RuntimeException("Unknown function: " + func);
                    }
                } else {
                    throw new RuntimeException("Unexpected: " + (char) ch);
                }
                if (eat('^')) {
                    x = Math.pow(x, parseFactor());
                }
                return x;

            }

        }.parse();
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //   تم تجهيز جميع الدوال التالية للتأكد قبل إدخال أي حرف, رقم أو رمز
    //   ملاحظة سيتم إستدعاء هذه الدوال في حال قام المستخدم بالنقر على الأزرار الموجودة في النافذة أو من لوحة المفاتيح
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // عند النقر على الزر 0 سيتم إضافة 0 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b0_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "0");
    }

    // عند النقر على الزر 1 سيتم إضافة 1 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b1_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "1");
    }

    // عند النقر على الزر 2 سيتم إضافة 2 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b2_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "2");
    }

    // عند النقر على الزر 3 سيتم إضافة 3 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b3_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "3");
    }

    // عند النقر على الزر 4 سيتم إضافة 4 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b4_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "4");
    }

    // عند النقر على الزر 5 سيتم إضافة 5 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b5_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "5");
    }

    // عند النقر على الزر 6 سيتم إضافة 6 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b6_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "6");
    }

    // عند النقر على الزر 7 سيتم إضافة 7 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b7_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "7");
    }

    // عند النقر على الزر 8 سيتم إضافة 8 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b8_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "8");
    }

    // عند النقر على الزر 9 سيتم إضافة 9 في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void b9_isClicked() {
        autoAddOrRemove("number");
        textField.setText(textField.getText() + "9");
    }

    // في مربع النص مع إضافةالعامل × قبلها إذا دعت الحاجة sin سيتم إضافة الكلمة sin عند النقر على الزر
    private void sin_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "sin");
    }

    // في مربع النص مع إضافةالعامل × قبلها إذا دعت الحاجة cos سيتم إضافة الكلمة cos عند النقر على الزر
    private void cos_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "cos");
    }

    // في مربع النص مع إضافةالعامل × قبلها إذا دعت الحاجة tan سيتم إضافة الكلمة tan عند النقر على الزر
    private void tan_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "tan");
    }

    // عند النقر على الزر √ سيتم إضافة √ في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void sqrt_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "√");
    }

    // في مربع النص مع إضافةالعامل × قبله إذا دعت الحاجة e سيتم إضافة e عند النقر على الزر
    private void exponential_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "e");
    }

    // في مربع النص مع إضافةالعامل × قبله إذا دعت الحاجة π سيتم إضافة π عند النقر على الزر
    private void pi_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "π");
    }

    // عند النقر على الزر + سيتم إضافة + في مربع النص
    private void plus_isClicked() {
        autoAddOrRemove("operand");
        textField.setText(textField.getText() + "+");
    }

    // عند النقر على الزر - سيتم إضافة - في مربع النص
    private void minus_isClicked() {
        autoAddOrRemove("operand");
        textField.setText(textField.getText() + "-");
    }

    // عند النقر على الزر × سيتم إضافة × في مربع النص
    private void multiple_isClicked() {
        if (!textField.getText().isEmpty()) {
            autoAddOrRemove("operand");
            textField.setText(textField.getText() + "×");
        }
    }

    // عند النقر على الزر ÷ سيتم إضافة ÷ في مربع النص
    private void divide_isClicked() {
        if (!textField.getText().isEmpty()) {
            autoAddOrRemove("operand");
            textField.setText(textField.getText() + "÷");
        }
    }

    // عند النقر على الزر % سيتم إضافة % في مربع النص
    private void modulo_isClicked() {
        if (!textField.getText().isEmpty()) {
            autoAddOrRemove("operand");
            textField.setText(textField.getText() + "%");
        }
    }

    // π أو e, عند النقر على الزر ^ سيتم إضافة ^ في مربع النص في حال كان آخر حرف فيه عبارة عن رقم
    private void power_isClicked() {
        if (textField.getText().matches(".*[0-9eπ)]$")) {
            textField.setText(textField.getText() + "^");
        }
    }

    // n أو s ,√ ,( عند النقر على الزر ( سيتم إضافة ( في مربع النص في حال لم يكن مربع نص فارغاً و في حال لم يكن آخر حرف فيه عبارة عن
    private void parentesesRight_isClicked() {
        if (textField.getText().matches(".*[^ns√(]$")) {
            int leftParentesesCounter = 0, rightParentesesCounter = 0;
            for (char c : textField.getText().toCharArray()) {
                if (c == '(') {
                    leftParentesesCounter++;
                } else if (c == ')') {
                    rightParentesesCounter++;
                }
            }
            if (leftParentesesCounter > rightParentesesCounter) {
                textField.setText(textField.getText() + ")");
            }
        }
    }

    // عند النقر على الزر ) سيتم إضافة ) في مربع النص مع إضافة العامل × قبله إذا دعت الحاجة
    private void parentesesLeft_isClicked() {
        autoAddOrRemove("symbol");
        textField.setText(textField.getText() + "(");
    }

    // عند النقر على زر النقطة ( أي الفاصلة ) سيتم إضافة نقطة في مربع النص مع إضافة 0 قبلها في حال كان مربع النص فارغاً
    private void comma_isClicked() {
        String str = textField.getText();
        if (textField.getText().isEmpty()) {
            textField.setText("0.");
        } else {
            int lastPointIndex = str.lastIndexOf(".");
            int lastPlusIndex = str.lastIndexOf("+");
            int lastMinusIndex = str.lastIndexOf("-");
            int lastMultipleIndex = str.lastIndexOf("×");
            int lastDivideIndex = str.lastIndexOf("÷");
            int lastModuloIndex = str.lastIndexOf("%");

            if (lastPointIndex <= lastPlusIndex
                    || lastPointIndex <= lastMinusIndex
                    || lastPointIndex <= lastMultipleIndex
                    || lastPointIndex <= lastDivideIndex
                    || lastPointIndex <= lastModuloIndex) {
                autoAddOrRemove("point");
                textField.setText(textField.getText() + ".");
            }
        }
    }

    // لتحليل المعادلة التي أدخلها المستخدم ثم عرض الناتج النهائي calculate() عند النقر على زر المساواة = سيتم إستدعاء الدالة
    private void equal_isClicked() {
        if (!textField.getText().isEmpty()) {
            String historyNewText = historyText.getText() + textField.getText() + "\n= ";
            try {
                Double answer = calculate(textField.getText());
                if (answer.isInfinite()) {
                    textField.setText("cannot divide by 0");
                    historyNewText += "cannot divide by 0";
                } else if (answer.isNaN()) {
                    textField.setText("Error");
                    historyNewText += "Error";
                } else {
                    textField.setText(format.format(answer));
                    historyNewText += format.format(answer);
                }
            } catch (Exception ex) {
                textField.setText("Error");
                historyNewText += "Error";
            }
            historyText.setText(historyNewText + "\n\n");
        }
    }

    // عند النقر على الزر ← سيتم مسح رقم, رمز أو كلمة واحدة من مربع النص أو مسح أي خطأ ظاهر
    private void back_isClicked() {
        String temp = textField.getText();
        if (temp.equals("Error") || temp.equals("cannot divide by 0")) {
            textField.setText("");
        } else if (!temp.isEmpty()) {
            temp = textField.getText().substring(0, textField.getText().length() - 1);
            if (temp.length() >= 2) {
                switch (temp.substring(temp.length() - 2)) {
                    case "co":
                    case "si":
                    case "ta":
                        temp = temp.substring(0, temp.length() - 2);
                        break;
                }
            }
            textField.setText(temp);
        }
    }

    // سيتم مسح كل شيء ظاهر و تصفير جميع القيم المخزنة للبدء من جديد C عند النقر على الزر
    private void clear_isClicked() {
        textField.setText("");
    }

    @Override
    public void start(Stage stage) {

        // هنا قمنا بإضافة كل عنصر داخل القائمة المناسبة له بالترتيب الذي نريد أن يتم عرضهم فيه
        menuBar.getMenus().addAll(view, edit, help);
        view.getItems().add(history);
        edit.getItems().addAll(copy, paste, new SeparatorMenuItem(), copyHistory, clearHistory);
        help.getItems().addAll(keyboardShortcuts, about);

        // هنا قمنا بتحديد حجم و موقع كل شيء أضفناه في النافذة
        historyText.setPrefSize(256, 311);
        historyText.setTranslateX(260);
        historyText.setTranslateY(33);
        textField.setPrefSize(234, 60);
        textField.setTranslateX(11);
        textField.setTranslateY(33);
        cos.setPrefSize(45, 38);
        cos.setTranslateX(10);
        cos.setTranslateY(101);
        sin.setPrefSize(45, 38);
        sin.setTranslateX(58);
        sin.setTranslateY(101);
        tan.setPrefSize(45, 38);
        tan.setTranslateX(106);
        tan.setTranslateY(101);
        back.setPrefSize(45, 38);
        back.setTranslateX(154);
        back.setTranslateY(101);
        clear.setPrefSize(45, 38);
        clear.setTranslateX(202);
        clear.setTranslateY(101);
        pi.setPrefSize(45, 38);
        pi.setTranslateX(10);
        pi.setTranslateY(142);
        exponential.setPrefSize(45, 38);
        exponential.setTranslateX(58);
        exponential.setTranslateY(142);
        modulo.setPrefSize(45, 38);
        modulo.setTranslateX(106);
        modulo.setTranslateY(142);
        parentesesLeft.setPrefSize(45, 38);
        parentesesLeft.setTranslateX(154);
        parentesesLeft.setTranslateY(142);
        parentesesRight.setPrefSize(45, 38);
        parentesesRight.setTranslateX(202);
        parentesesRight.setTranslateY(142);
        b7.setPrefSize(45, 38);
        b7.setTranslateX(10);
        b7.setTranslateY(183);
        b8.setPrefSize(45, 38);
        b8.setTranslateX(58);
        b8.setTranslateY(183);
        b9.setPrefSize(45, 38);
        b9.setTranslateX(106);
        b9.setTranslateY(183);
        plus.setPrefSize(45, 38);
        plus.setTranslateX(154);
        plus.setTranslateY(183);
        power.setPrefSize(45, 38);
        power.setTranslateX(202);
        power.setTranslateY(183);
        b4.setPrefSize(45, 38);
        b4.setTranslateX(10);
        b4.setTranslateY(224);
        b5.setPrefSize(45, 38);
        b5.setTranslateX(58);
        b5.setTranslateY(224);
        b6.setPrefSize(45, 38);
        b6.setTranslateX(106);
        b6.setTranslateY(224);
        minus.setPrefSize(45, 38);
        minus.setTranslateX(154);
        minus.setTranslateY(224);
        sqrt.setPrefSize(45, 38);
        sqrt.setTranslateX(202);
        sqrt.setTranslateY(224);
        b1.setPrefSize(45, 38);
        b1.setTranslateX(10);
        b1.setTranslateY(265);
        b2.setPrefSize(45, 38);
        b2.setTranslateX(58);
        b2.setTranslateY(265);
        b3.setPrefSize(45, 38);
        b3.setTranslateX(106);
        b3.setTranslateY(265);
        multiple.setPrefSize(45, 38);
        multiple.setTranslateX(154);
        multiple.setTranslateY(265);
        equal.setPrefSize(45, 79);
        equal.setTranslateX(202);
        equal.setTranslateY(265);
        b0.setPrefSize(93, 38);
        b0.setTranslateX(10);
        b0.setTranslateY(306);
        comma.setPrefSize(45, 38);
        comma.setTranslateX(106);
        comma.setTranslateY(306);
        divide.setPrefSize(45, 38);
        divide.setTranslateX(154);
        divide.setTranslateY(306);
        menuBar.setPrefSize(5000, 20);

        // هنا قمنا بتحديد حجم و نوع خط النص الذي سيتم إدخاله في مربع النص بالإضافة إلى جعل النص يبدأ بالظهور من اليمين إلى اليسار
        textField.setAlignment(Pos.CENTER_RIGHT);

		// بعد أن نربطه فيه style.css لكل عنصر في النافذة حتى يتم تطبيق التصميم الذي وضعناه لهم في الملف Id هنا قمنا بإعطاء إسم
        b0.setId("b0");
        b1.setId("b1");
        b2.setId("b2");
        b3.setId("b3");
        b4.setId("b4");
        b5.setId("b5");
        b6.setId("b6");
        b7.setId("b7");
        b8.setId("b8");
        b9.setId("b9");
        comma.setId("comma");
        plus.setId("plus");
        minus.setId("minus");
        multiple.setId("multiple");
        divide.setId("divide");
        equal.setId("equal");
        cos.setId("cos");
        sin.setId("sin");
        tan.setId("tan");
        pi.setId("pi");
        exponential.setId("exponential");
        modulo.setId("modulo");
        parentesesLeft.setId("parentesesLeft");
        parentesesRight.setId("parentesesRight");
        sqrt.setId("sqrt");
        power.setId("power");
        clear.setId("clear");
        back.setId("back");
        textField.setId("textField");
        historyText.setId("historyText");
        menuBar.setId("menuBar");
        root.setId("root");

        // هنا قلنا أنه لن يتم البقاء فوق أي زر أو مربع نص بعد النقر عليه حتى يظل المستخدم قادراً على إستخدام لوحة المفاتيح
		// Enter فمثلاً لكي يتم طباعة جواب العملية الحسابية المدخلة في حال قام المستخدم بالنقر على الزر
        textField.setFocusTraversable(false);
		textField.setEditable(false);
        b0.setFocusTraversable(false);
        b1.setFocusTraversable(false);
        b2.setFocusTraversable(false);
        b3.setFocusTraversable(false);
        b4.setFocusTraversable(false);
        b5.setFocusTraversable(false);
        b6.setFocusTraversable(false);
        b7.setFocusTraversable(false);
        b8.setFocusTraversable(false);
        b9.setFocusTraversable(false);
        equal.setFocusTraversable(false);
        clear.setFocusTraversable(false);
        back.setFocusTraversable(false);
        sin.setFocusTraversable(false);
        cos.setFocusTraversable(false);
        tan.setFocusTraversable(false);
        pi.setFocusTraversable(false);
        exponential.setFocusTraversable(false);
        modulo.setFocusTraversable(false);
        parentesesLeft.setFocusTraversable(false);
        parentesesRight.setFocusTraversable(false);
        sqrt.setFocusTraversable(false);
        power.setFocusTraversable(false);
        multiple.setFocusTraversable(false);
        plus.setFocusTraversable(false);
        minus.setFocusTraversable(false);
        comma.setFocusTraversable(false);
        historyText.setFocusTraversable(false);
        historyText.setEditable(false);

        // يمثل ما يحدث عند النقر على أي زر موضوع في البرنامج EventHandler هنا قمنا بتعريف كائن من الإنترفيس
        EventHandler<ActionEvent> eventHandler = (ActionEvent e) -> {
            actionPerformed(e);
        };

		// eventHandler هنا قمنا بربط جميع الأزرار التي وضعناه في النافذة و في شريط القوائم بالكائن
        // e عند النقر على أي زر موجود في النافذة مع تمرير الكائن الذي يمثل الشيء تم النقر عليه مكان المتغير actionPerformed() حتى يتم إستدعاء الدالة
        history.addEventHandler(ActionEvent.ACTION, eventHandler);
        copy.addEventHandler(ActionEvent.ACTION, eventHandler);
        paste.addEventHandler(ActionEvent.ACTION, eventHandler);
        copyHistory.addEventHandler(ActionEvent.ACTION, eventHandler);
        clearHistory.addEventHandler(ActionEvent.ACTION, eventHandler);
        keyboardShortcuts.addEventHandler(ActionEvent.ACTION, eventHandler);
        about.addEventHandler(ActionEvent.ACTION, eventHandler);
        b0.addEventHandler(ActionEvent.ACTION, eventHandler);
        b1.addEventHandler(ActionEvent.ACTION, eventHandler);
        b2.addEventHandler(ActionEvent.ACTION, eventHandler);
        b3.addEventHandler(ActionEvent.ACTION, eventHandler);
        b4.addEventHandler(ActionEvent.ACTION, eventHandler);
        b5.addEventHandler(ActionEvent.ACTION, eventHandler);
        b6.addEventHandler(ActionEvent.ACTION, eventHandler);
        b7.addEventHandler(ActionEvent.ACTION, eventHandler);
        b8.addEventHandler(ActionEvent.ACTION, eventHandler);
        b9.addEventHandler(ActionEvent.ACTION, eventHandler);
        comma.addEventHandler(ActionEvent.ACTION, eventHandler);
        exponential.addEventHandler(ActionEvent.ACTION, eventHandler);
        pi.addEventHandler(ActionEvent.ACTION, eventHandler);
        cos.addEventHandler(ActionEvent.ACTION, eventHandler);
        sin.addEventHandler(ActionEvent.ACTION, eventHandler);
        tan.addEventHandler(ActionEvent.ACTION, eventHandler);
        sqrt.addEventHandler(ActionEvent.ACTION, eventHandler);
        power.addEventHandler(ActionEvent.ACTION, eventHandler);
        modulo.addEventHandler(ActionEvent.ACTION, eventHandler);
        parentesesLeft.addEventHandler(ActionEvent.ACTION, eventHandler);
        parentesesRight.addEventHandler(ActionEvent.ACTION, eventHandler);
        plus.addEventHandler(ActionEvent.ACTION, eventHandler);
        multiple.addEventHandler(ActionEvent.ACTION, eventHandler);
        divide.addEventHandler(ActionEvent.ACTION, eventHandler);
        minus.addEventHandler(ActionEvent.ACTION, eventHandler);
        equal.addEventHandler(ActionEvent.ACTION, eventHandler);
        clear.addEventHandler(ActionEvent.ACTION, eventHandler);
        back.addEventHandler(ActionEvent.ACTION, eventHandler);

        // root هنا أضفنا شريط القوائم, و مربعات النصوص و جميع الأزرار التي قمنا بتعريفها و تحديد خصائصها في الحاوية الأساسية في النافذة و التي إسمها
        root.getChildren().add(menuBar);
        root.getChildren().add(b0);
        root.getChildren().add(b1);
        root.getChildren().add(b2);
        root.getChildren().add(b3);
        root.getChildren().add(b4);
        root.getChildren().add(b5);
        root.getChildren().add(b6);
        root.getChildren().add(b7);
        root.getChildren().add(b8);
        root.getChildren().add(b9);
        root.getChildren().add(comma);
        root.getChildren().add(equal);
        root.getChildren().add(plus);
        root.getChildren().add(multiple);
        root.getChildren().add(minus);
        root.getChildren().add(divide);
        root.getChildren().add(cos);
        root.getChildren().add(sin);
        root.getChildren().add(tan);
        root.getChildren().add(sqrt);
        root.getChildren().add(power);
        root.getChildren().add(modulo);
        root.getChildren().add(exponential);
        root.getChildren().add(pi);
        root.getChildren().add(parentesesRight);
        root.getChildren().add(parentesesLeft);
        root.getChildren().add(clear);
        root.getChildren().add(back);
        root.getChildren().add(textField);
        root.getChildren().add(historyText);

        // فيها و تحديد حجمها Node كأول root هنا قمنا بإنشاء محتوى النافذة مع تعيين الكائن
        Scene scene = new Scene(root, 247, 343);
		
		// scene بالكائن css الذي وضعناه في المجلد style.css هنا قمنا بربط الملف
		// scene هكذا سيتم تطبيق كود التصميم الموضوع فيه على الأشياء الموضوعة في الكائن 
        scene.getStylesheets().add("css/style.css");

		// كالكائن الذي سيمثل نافذة التطبيق myStage هنا قمنا بوضع الكائن
        myStage = stage;
        // هنا وضعنا عنوان للنافذة
        myStage.setTitle("Scientific Calculator");
        // أي وضعنا محتوى النافذة الذي قمنا بإنشائه للنافذة .stage في كائن الـ scene هنا وضعنا كائن الـ
        myStage.setScene(scene);
        // هنا قمنا بإظهار النافذة
        myStage.show();
		// هنا جعلنا المستخدم غير قادر على تكبير و تصغير حجم الشاشة بشكل يدوي
        myStage.setResizable(false);

        // عند الضغط على أي حرف من لوحة المفاتيح سيتم تنفيذ الأوامر الموضوعة هنا
        // e سيتم تمرير يمرر الحرف الذي تم النقر عليه مكان الكائن
        scene.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent e) -> {
			
            if (null != e.getCode()) {
				// لاحظ أنه على أساس الحرف المدخل سيتم إستدعاء الدالة المناسبة و التي بدورها ستكتبه في مربع النص
                switch (e.getCode())
				{
                    case NUMPAD0:         // هذا الثابت يمثل الرقم 0
                        b0_isClicked();
                        break;

                    case NUMPAD1:         // هذا الثابت يمثل الرقم 1
                        b1_isClicked();
                        break;

                    case NUMPAD2:         // هذا الثابت يمثل الرقم 2
                        b2_isClicked();
                        break;

                    case NUMPAD3:         // هذا الثابت يمثل الرقم 3
                        b3_isClicked();
                        break;

                    case NUMPAD4:         // هذا الثابت يمثل الرقم 4
                        b4_isClicked();
                        break;

                    case NUMPAD5:         // هذا الثابت يمثل الرقم 5
                        b5_isClicked();
                        break;

                    case NUMPAD6:         // هذا الثابت يمثل الرقم 6
                        b6_isClicked();
                        break;

                    case NUMPAD7:         // هذا الثابت يمثل الرقم 7
                        b7_isClicked();
                        break;

                    case NUMPAD8:         // هذا الثابت يمثل الرقم 8
                        b8_isClicked();
                        break;

                    case NUMPAD9:         // هذا الثابت يمثل الرقم 9
                        b9_isClicked();
                        break;

                    case S:               // s هذا الثابت يمثل الحرف
                        sin_isClicked();
                        break;

                    case C:               // c هذا الثابت يمثل الحرف
                        cos_isClicked();
                        break;

                    case T:               // t هذا الثابت يمثل الحرف
                        tan_isClicked();
                        break;

                    case V:               // v هذا الثابت يمثل الحرف
                        sqrt_isClicked();
                        break;

                    case E:               // e هذا الثابت يمثل الحرف
                        exponential_isClicked();
                        break;

                    case P:               // p هذا الثابت يمثل الحرف
                        pi_isClicked();
                        break;

				    // Shift ملاحظة: الأحرف التالية لا تطلب النقر على الزر
					
                    case PLUS:            // + هذا الثابت يمثل الرمز
                        plus_isClicked();
                        break;

                    case MINUS:           // - هذا الثابت يمثل الرمز
                        minus_isClicked();
                        break;

                    case MULTIPLY:        // * هذا الثابت يمثل الرمز
                        multiple_isClicked();
                        break;

                    case DIVIDE:          // / هذا الثابت يمثل الرمز
                        divide_isClicked();
                        break;

                    case DIGIT5:          // % هذا الثابت يمثل الرمز
                        modulo_isClicked();
                        break;

                    case DIGIT6:          // ^ هذا الثابت يمثل الرمز
                        power_isClicked();
                        break;

                    case DIGIT0:          // ) هذا الثابت يمثل الرمز
                        parentesesRight_isClicked();
                        break;

                    case DIGIT9:          // ( هذا الثابت يمثل الرمز
                        parentesesLeft_isClicked();
                        break;

                    case COMMA:           // . هذا الثابت يمثل الرمز
                        comma_isClicked();
                        break;

                    case ENTER:           // Enter هذا الثابت يمثل الزر
                        equal_isClicked();
                        break;

                    case BACK_SPACE:      // BackSpace هذا الثابت يمثل الزر
                        back_isClicked();
                        break;

                    case DELETE:          // Del هذا الثابت يمثل الزر
                        clear_isClicked();
                        break;
                }
            }
        });
    }

    // هنا ربطنا الدوال التي قمنا بتعريفها سابقاً بجميع الأزرار الموجودة في النافذة
    // بكل بساطة, الزر الذي يتم النقر عليه سيقوم بتنفيذ الدالة التي صممت لأجله
    // في الأخير قمنا بتعريف ما يحدث عند النقر على أي زر موجود في شريط القوائم
    public void actionPerformed(ActionEvent e) {
		
        if (e.getSource() == b0) 
            b0_isClicked();
         
		else if (e.getSource() == b1) 
           b1_isClicked();
	   
        else if (e.getSource() == b2) 
           b2_isClicked();
        
		else if (e.getSource() == b3) 
           b3_isClicked();
        
		else if (e.getSource() == b4) 
           b4_isClicked();
        
		else if (e.getSource() == b5) 
           b5_isClicked();
        
		else if (e.getSource() == b6) 
           b6_isClicked();
        
		else if (e.getSource() == b7) 
           b7_isClicked();
        
		else if (e.getSource() == b8) 
           b8_isClicked();
        
		else if (e.getSource() == b9) 
           b9_isClicked();
        
		else if (e.getSource() == sin) 
           sin_isClicked();
        
		else if (e.getSource() == cos) 
           cos_isClicked();
        
		else if (e.getSource() == tan) 
           tan_isClicked();
        
		else if (e.getSource() == sqrt) 
           sqrt_isClicked();
        
		else if (e.getSource() == exponential) 
           exponential_isClicked();
        
		else if (e.getSource() == pi) 
           pi_isClicked();
        
		else if (e.getSource() == plus) 
           plus_isClicked();
        
		else if (e.getSource() == minus) 
           minus_isClicked();
        
		else if (e.getSource() == multiple) 
           multiple_isClicked();
        
		else if (e.getSource() == divide) 
           divide_isClicked();
        
		else if (e.getSource() == modulo) 
           modulo_isClicked();
        
		else if (e.getSource() == power) 
           power_isClicked();
        
		else if (e.getSource() == parentesesRight) 
           parentesesRight_isClicked();
        
		else if (e.getSource() == parentesesLeft) 
           parentesesLeft_isClicked();
        
		else if (e.getSource() == comma) 
           comma_isClicked();
        
		else if (e.getSource() == equal) 
           equal_isClicked();
        
		else if (e.getSource() == back) 
           back_isClicked();
        
		else if (e.getSource() == clear) 
           clear_isClicked();
        
		// من شريط القوائم سيتم تكبير حجم النافذة لإظهاره history إذا تم تفعيل الـ
        else if (e.getSource() == history) {
            if (history.isSelected())
                myStage.setWidth(532);
            else
                myStage.setWidth(263);
        }
		
		// الموجود في شريط القوائم سيتم نسخ المعادلة المدخلة في مربع النص copy عند النقر على الزر
        else if (e.getSource() == copy) {
            textField.selectAll();
            textField.copy();
            textField.positionCaret(textField.getText().length());
        }
		
		// الموجود في شريط القوائم سيتم لصق أي نص تم نسخه سابقاً في آخر مربع النص paste عند النقر على الزر
        else if (e.getSource() == paste) {
            textField.paste();
        }
		
		// الموجود في شريط القوائم سيتم نسخ جميع المعادلات و الإجابات المخزنة سابقاً copyHistory عند النقر على الزر
        else if (e.getSource() == copyHistory) {
            historyText.selectAll();
            historyText.copy();
            textField.positionCaret(textField.getText().length());
        }
		
		// الموجود في شريط القوائم سيتم مسح جميع المعادلات و الإجابات المخزنة سابقاً clearHistory عند النقر على الزر
        else if (e.getSource() == clearHistory) {
            historyText.setText("");
        }
		
		// الموجود في شريط القوائم سيتم إظهار نافذة منبثقة تحتوي على إختصارات لوحة المفاتيح keyboardShortcuts عند النقر على الزر
        else if (e.getSource() == keyboardShortcuts) {
            String str
                    = "Press  V  to add √.\n"
                    + "Press  P  to add π.\n"
                    + "Press  C  to add cos.\n"
                    + "Press  S  to add sin.\n"
                    + "Press  T  to add tan.\n"
                    + "Press  Enter  to get the result\n"
                    + "Press  BackSpace  to clear last character entered.\n"
                    + "Press  Delete  to clear all characters entered.";

            // و من ثم قمنا بإظهارها alert هنا قمنا بتمرير النص الذي نريد وضعه في النافذة المنبثقة
            alert.setTitle("Keyboard Shortcuts");
            alert.setHeaderText("Keyboard Shortcuts");
            alert.setContentText(str);
            alert.showAndWait();
        }
		
		// الموجود في شريط القوائم سيتم إظهار نافذة منبثقة فيها معلومات حول البرنامج about عند النقر على الزر
        else if (e.getSource() == about) {
            String str
                    = "Prepared by Mhamad Harmush\n\n"
                    + "If you have any comments, ideas.. just let me know\n\n"
                    + "Email:   mhamad.harmush@gmail.com\n"
                    + "Twitter & Facebook:   @MhamadHarmush\n\n"
                    + "Note\n"
                    + "I used JDK 1.8 to compile the source code.\n\n"
                    + "© Copyright 2019 harmash.com - All Rights Reserved";

            // و من ثم قمنا بإظهارها alert هنا قمنا بتمرير النص الذي نريد وضعه في النافذة المنبثقة
            alert.setTitle("About");
            alert.setHeaderText("About");
            alert.setContentText(str);
            alert.showAndWait();
        }
		
    }

    public static void main(String[] args) {
        launch(args);
    }

}
		

هذه الصور جميعها من البرنامج.


ملاحظة

يمكنك إضافة مزيد من الشروط لتحديد كيف و متى سيتم إدخال الأرقام و الرموز.
و لقد تعمدنا عدم تغطية جميع الإحتمالات حتى لا يصبح حجم الكود كبير جداً و معقد أكثر.




كورس تعلم javaFX