Skip to content

devkesav/calc-on-java

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Simple Calc

Calculator icon

  • This is basic calcuator desktop application developed in java 17.
  • It has the basic functions like additionsubtractionmultiplicationdivision and square root.
  • and The UI design is inspired from Iphone Calculator.

for documentation i used AI to generate the md code but the content is mine.

A desktop calculator built with Java Swing/AWT, supporting both mouse clicks and full keyboard input.


1. Overview

Aspect Detail
UI Toolkit javax.swing.* (components) + java.awt.* (layout, events)
Entry point Calculator class, instantiated from App.java
Input methods Mouse clicks (ActionListener) and keyboard (KeyListener)
Operations +, -, *, /, %, +/-, , decimal, backspace, clear
Display style Live expression view — e.g. typing 10 + 10 shows exactly that before = collapses it to 20

2. Class structure

classDiagram
    class Calculator {
        -JFrame frame
        -JLabel displayLabel
        -JPanel displayPanel
        -JPanel buttonsPanel
        -String A
        -String currentInput
        -String operator
        -boolean waitingForSecondOperand
        -boolean resultDisplayed
        +Calculator()
        -clearAll() void
        -refreshDisplay() void
        -calculateAndShowResult() void
        -removeZeroDecimal(double) String
        -handleInput(String) void
        -handleBackspace() void
        +keyPressed(KeyEvent) void
        +keyReleased(KeyEvent) void
        +keyTyped(KeyEvent) void
    }
    Calculator ..|> KeyListener : implements
Loading

Why implements KeyListener?
This lets Calculator itself act as its own keyboard event handler. The class must then provide bodies for all three interface methods (keyPressed, keyReleased, keyTyped) — only keyPressed does real work here, the other two are required but empty.


3. UI Component Hierarchy — JFrame / JPanel structure

This shows the actual Swing container tree — what's nested inside what, and which layout manager governs each level.

flowchart TD
    Frame["JFrame 'frame'<br/>BorderLayout<br/>360x540"]

    Frame -->|BorderLayout.NORTH| DisplayPanel["JPanel 'displayPanel'<br/>BorderLayout"]
    Frame -->|BorderLayout.CENTER default| ButtonsPanel["JPanel 'buttonsPanel'<br/>GridLayout 5x4<br/>black background"]

    DisplayPanel --> DisplayLabel["JLabel 'displayLabel'<br/>right-aligned, white text<br/>Font 80pt<br/>shows current expression"]

    ButtonsPanel --> B1["JButton AC"]
    ButtonsPanel --> B2["JButton +/-"]
    ButtonsPanel --> B3["JButton %"]
    ButtonsPanel --> B4["JButton /"]
    ButtonsPanel --> B5["JButton 7"]
    ButtonsPanel --> B6["JButton 8"]
    ButtonsPanel --> B7["... 12 more buttons"]
    ButtonsPanel --> B8["JButton ="]

    style Frame fill:#1c1c1c,color:#fff
    style DisplayPanel fill:#333,color:#fff
    style ButtonsPanel fill:#333,color:#fff
    style DisplayLabel fill:#000,color:#fff
Loading

How the layout actually renders:

flowchart TD
    subgraph JFrame["JFrame (BorderLayout)"]
        direction TB
        subgraph North["NORTH region"]
            direction TB
            subgraph DP["JPanel: displayPanel"]
                DL["JLabel: displayLabel<br/>'10 + 10'"]
            end
        end
        subgraph Center["CENTER region"]
            direction TB
            subgraph BP["JPanel: buttonsPanel (GridLayout 5x4)"]
                direction TB
                Row1["AC | +/- | % | /"]
                Row2["7 | 8 | 9 | x"]
                Row3["4 | 5 | 6 | -"]
                Row4["1 | 2 | 3 | +"]
                Row5["0 | . | √ | ="]
                Row1 ~~~ Row2 ~~~ Row3 ~~~ Row4 ~~~ Row5
            end
        end
        North --> Center
    end
Loading

Why BorderLayout on the frame, but GridLayout on the buttons panel?

  • frame.setLayout(new BorderLayout()) divides the window into up to 5 regions (NORTH, SOUTH, EAST, WEST, CENTER). Only NORTH (the display) and the default CENTER (the buttons panel) are used here — displayPanel is pinned to the top and stays a fixed height, while buttonsPanel fills all remaining space.
  • buttonsPanel.setLayout(new GridLayout(5, 4)) then arranges its own children (the 20 buttons) into a strict 5-row, 4-column grid, all cells equal size — which is why the buttons form a clean uniform keypad.

This is a common and correct Swing pattern: nest a simple layout inside a region of a more flexible outer layout, rather than trying to make one layout manager do everything.


4. State variables — what they actually track

Variable Meaning
A The first operand, captured the moment an operator is pressed
currentInput Whatever number is currently being typed (first operand before an operator, or second operand after)
operator The pending operation (+, -, *, /), or null if none chosen yet
waitingForSecondOperand true right after an operator is pressed, before the user types any digit for the second number
resultDisplayed true right after = — lets the next digit typed start a fresh calculation instead of appending to the old result

5. Construction flow (what happens when new Calculator() runs)

flowchart TD
    A[Constructor starts] --> B[Configure frame: size, icon, layout]
    B --> C[Build displayLabel + displayPanel]
    C --> D[Build buttonsPanel with GridLayout 5x4]
    D --> E[Loop through buttonValues array]
    E --> F[Create JButton, set color by category]
    F --> G[Add ActionListener → calls handleInput]
    G --> H{More buttons?}
    H -- yes --> E
    H -- no --> I[frame.setVisible true]
    I --> J[frame.requestFocusInWindow]
    J --> K[frame.addKeyListener this]
    K --> L[Calculator ready]
Loading

Order matters heresetVisible(true) must come after every button is added, otherwise the window can render partially (a bug this project hit earlier). Similarly, addKeyListener(this) must be called exactly once, not inside the button loop, or every keypress fires multiple times.


6. Button color logic

flowchart TD
    Start[For each buttonValue] --> Check1{In topSymbols?<br/>AC, +/-, %}
    Check1 -- yes --> Light[Light gray background<br/>black text]
    Check1 -- no --> Check2{In rightSymbols?<br/>+ - * / =}
    Check2 -- yes --> Orange[Orange background<br/>white text]
    Check2 -- no --> Dark[Dark gray background<br/>white text]
Loading

7. handleInput() — the core decision tree

This single method receives a String value (either from a button's text or from a keyboard mapping) and routes it based on what kind of input it is.

flowchart TD
    Start["handleInput(value)"] --> Q1{value is +,-,*,/,=?}
    Q1 -- yes --> Q2{value == '='?}
    Q2 -- yes --> Q3{operator and A set?}
    Q3 -- yes --> Calc[calculateAndShowResult]
    Q3 -- no --> Ignore1[do nothing]
    Q2 -- no --> Q4{operator == null?}
    Q4 -- yes --> SetFirst[A = currentInput<br/>operator = value<br/>waitingForSecondOperand = true]
    Q4 -- no --> Q5{waitingForSecondOperand?}
    Q5 -- yes --> SwapOp[operator = value<br/>just changes the pending operator]
    Q5 -- no --> ChainCalc[calculateAndShowResult<br/>then start new operation<br/>with previous result as A]

    Q1 -- no --> Q6{value is AC, +/-, or %?}
    Q6 -- yes --> TopOps[Clear / negate / divide by 100]
    Q6 -- no --> Q7{value == '√'?}
    Q7 -- yes --> Sqrt[Square root of currentInput]
    Q7 -- no --> Digits[Digit or decimal point<br/>appended to currentInput]

    SetFirst --> Refresh[refreshDisplay]
    SwapOp --> Refresh
    ChainCalc --> Refresh
    TopOps --> Refresh
    Sqrt --> Refresh
    Digits --> Refresh
    Calc --> End[Done]
    Refresh --> End
Loading

8. Display behavior — the exact sequence you asked about

Typing 10, pressing +, typing 10, pressing =:

sequenceDiagram
    participant User
    participant handleInput
    participant State as A / operator / currentInput
    participant Display as displayLabel

    User->>handleInput: "1"
    handleInput->>State: currentInput = "1"
    handleInput->>Display: refreshDisplay → "1"

    User->>handleInput: "0"
    handleInput->>State: currentInput = "10"
    handleInput->>Display: refreshDisplay → "10"

    User->>handleInput: "+"
    handleInput->>State: A = "10", operator = "+"<br/>waitingForSecondOperand = true
    handleInput->>Display: refreshDisplay → "10 +"

    User->>handleInput: "1"
    handleInput->>State: currentInput = "1"<br/>waitingForSecondOperand = false
    handleInput->>Display: refreshDisplay → "10 + 1"

    User->>handleInput: "0"
    handleInput->>State: currentInput = "10"
    handleInput->>Display: refreshDisplay → "10 + 10"

    User->>handleInput: "="
    handleInput->>State: calculateAndShowResult → A = "20"<br/>operator = null, resultDisplayed = true
    handleInput->>Display: displayLabel.setText → "20"
Loading

This is handled by refreshDisplay(), which checks three states:

flowchart TD
    R[refreshDisplay called] --> C1{operator == null?}
    C1 -- yes --> Show1[Show currentInput only<br/>e.g. '10']
    C1 -- no --> C2{waitingForSecondOperand?}
    C2 -- yes --> Show2[Show 'A operator'<br/>e.g. '10 +']
    C2 -- no --> Show3[Show 'A operator currentInput'<br/>e.g. '10 + 10']
Loading

9. Keyboard input flow

flowchart TD
    Key[Key pressed] --> Shift{Shift+5?}
    Shift -- yes --> Percent[handleInput '%'] --> Stop[return]
    Shift -- no --> Switch{Switch on getKeyCode}
    Switch -- Backspace --> BS[handleBackspace]
    Switch -- Enter --> Eq[handleInput '=']
    Switch -- Escape --> AC[handleInput 'AC']
    Switch -- Numpad +/-/*// --> Ops[handleInput operator]
    Switch -- F1 --> Sqrt[handleInput '√']
    Switch -- default --> Digit{isDigit or '.'?}
    Digit -- yes --> DigitCall[handleInput char]
    Digit -- no --> Nothing[ignored]
Loading

Why addKeyListener(this) is required: implementing the KeyListener interface only gives the class the methods — it does nothing until you register an actual object as a listener on a component. frame.addKeyListener(this) tells Swing "call these methods on this exact Calculator instance whenever the frame receives key events."


10. Full user interaction — click and keyboard converge on the same path

flowchart LR
    Mouse[Mouse click on JButton] --> AL[ActionListener]
    Keyboard[Key press] --> KL[keyPressed switch]
    AL --> HI[handleInput value]
    KL --> HI
    HI --> RD[refreshDisplay / calculateAndShowResult]
    RD --> UI[displayLabel updates]
Loading

Both input paths converge on the same handleInput() method — this is intentional and important: it guarantees mouse clicks and keyboard presses always produce identical behavior, since there's only one place the actual calculator logic lives.


11. Known limitations / things to watch

  • Dividing by zero (10 / 0) produces Infinity on screen rather than a friendly error — not currently guarded.
  • Top-row +/-/*// keys (not numpad) fall through to the default case and rely on Character.isDigit() filtering them out — they are not currently wired to trigger operators, only numpad VK_ADD/VK_SUBTRACT/VK_MULTIPLY/VK_DIVIDE are.
  • of a negative number sets currentInput to "Error" but doesn't lock further input — typing digits afterward will append to "Error" rather than resetting cleanly.

About

I wrote this source code on my own, not by vibe coding to ensure my java learning

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages