Building a Calculator App¶
| Difficulty | Beginner | Time | 30 minutes~ |
1. Set the background color¶

- Use
Scene::SetBackground(color);to set the background color.
Code
2. Prepare button information¶

- Create a
Buttonclass that holds aRectrepresenting the button area and aStringrepresenting the text on the button. - Set the information for the "1" button as the initial value of the
Buttonclass variablebutton.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Button information
Button button{ Rect{ 100, 200, 90, 90 }, U"1" };
while (System::Update())
{
}
}
3. Draw the button background¶

- Access the
.rectmember variable of theButtonclass and draw the button background.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Button information
Button button{ Rect{ 100, 200, 90, 90 }, U"1" };
while (System::Update())
{
// Draw the button background
button.rect.draw();
}
}
4. Round the button corners¶

- Use the
Rectmember function.rounded(corner_radius)to round the button corners.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Button information
Button button{ Rect{ 100, 200, 90, 90 }, U"1" };
while (System::Update())
{
// Draw the button background
button.rect.rounded(8).draw();
}
}
5. Display text on the button¶

- Prepare a
Fontfor displaying text. - Access the
.textmember variable of theButtonand draw the button text.
New Features
font(text).drawAt(font_size, position, color);draws text centered at the specified position.- The
.center()member function ofRectreturns the coordinates of the rectangle's center.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Button information
Button button{ Rect{ 100, 200, 90, 90 }, U"1" };
while (System::Update())
{
// Draw the button background
button.rect.rounded(8).draw();
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), ColorF{ 0.1 });
}
}
6. Change the mouse cursor to a hand over buttons¶

- Use the
Rectmember function.mouseOver()to check if the mouse cursor is over the button. - Use
Cursor::RequestStyle(CursorStyle::Hand);to change the current mouse cursor style to a hand.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Button information
Button button{ Rect{ 100, 200, 90, 90 }, U"1" };
while (System::Update())
{
// Draw the button background
button.rect.rounded(8).draw();
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), ColorF{ 0.1 });
// When the mouse is over the button
if (button.rect.mouseOver())
{
// Change the mouse cursor to a hand
Cursor::RequestStyle(CursorStyle::Hand);
}
}
}
7. Input formulas with buttons¶

- Prepare a
Stringtype variableexpressionto manage the input formula for the calculator. - When the button is clicked, add the button's text "U\"1\"" to
expression. - Use
Printto display the contents of the input formula briefly.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Button information
Button button{ Rect{ 100, 200, 90, 90 }, U"1" };
// Input expression
String expression;
while (System::Update())
{
// Draw the button background
button.rect.rounded(8).draw();
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), ColorF{ 0.1 });
// When the mouse is over the button
if (button.rect.mouseOver())
{
// Change the mouse cursor to a hand
Cursor::RequestStyle(CursorStyle::Hand);
}
// When the button is clicked
if (button.rect.leftClicked())
{
// Add the button text to the expression
expression += button.text;
}
ClearPrint();
Print << expression; // Display the expression briefly
}
}
8. Add more buttons¶

- Use the
Array<Button>array to manage more buttons. - Write the processing for each button inside
for (const auto& button : buttons) { }. - When a button is clicked, update the input expression
expressionaccording to that button's text.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Array of button information
Array<Button> buttons;
buttons << Button{ Rect{ 100, 200, 90, 90 }, U"1" };
buttons << Button{ Rect{ 200, 200, 90, 90 }, U"2" };
buttons << Button{ Rect{ 300, 200, 90, 90 }, U"3" };
buttons << Button{ Rect{ 400, 200, 90, 90 }, U"+" };
buttons << Button{ Rect{ 400, 300, 90, 90 }, U"=" };
buttons << Button{ Rect{ 400, 400, 90, 90 }, U"C" };
// Input expression
String expression;
while (System::Update())
{
// For each button
for (const auto& button : buttons)
{
// Draw the button background
button.rect.rounded(8).draw();
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), ColorF{ 0.1 });
// When the mouse is over the button
if (button.rect.mouseOver())
{
// Change the mouse cursor to a hand
Cursor::RequestStyle(CursorStyle::Hand);
}
// When the button is clicked
if (button.rect.leftClicked())
{
if (button.text == U"=")
{
// ToDo
}
else if (button.text == U"+")
{
expression += button.text;
}
else if (button.text == U"C")
{
// Clear the expression
expression.clear();
}
else
{
// Add the button text to the expression
expression += button.text;
}
}
}
ClearPrint();
Print << expression; // Display the expression briefly
}
}
9. Add an expression display area¶

- Add an area to the calculator for displaying expressions.
- Display the expression right-aligned, shifted 10 pixels to the left from the display area.
New Features
font(text).draw(font_size, Arg::rightCenter = position, color);draws text right-aligned with the specified position as the center of the right edge of the text.- The
.rightCenter()member function ofRectreturns the coordinates of the center of the rectangle's right edge.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Result display area color
const ColorF displayColor{ 0.8, 0.9, 0.8 };
// Result display area text color
const ColorF displayTextColor{ 0.1 };
// Result display area rectangle
const Rect displayRect{ 100, 100, 390, 90 };
// Array of button information
Array<Button> buttons;
buttons << Button{ Rect{ 100, 200, 90, 90 }, U"1" };
buttons << Button{ Rect{ 200, 200, 90, 90 }, U"2" };
buttons << Button{ Rect{ 300, 200, 90, 90 }, U"3" };
buttons << Button{ Rect{ 400, 200, 90, 90 }, U"+" };
buttons << Button{ Rect{ 400, 300, 90, 90 }, U"=" };
buttons << Button{ Rect{ 400, 400, 90, 90 }, U"C" };
// Input expression
String expression;
while (System::Update())
{
// For each button
for (const auto& button : buttons)
{
// Draw the button background
button.rect.rounded(8).draw();
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), ColorF{ 0.1 });
// When the mouse is over the button
if (button.rect.mouseOver())
{
// Change the mouse cursor to a hand
Cursor::RequestStyle(CursorStyle::Hand);
}
// When the button is clicked
if (button.rect.leftClicked())
{
if (button.text == U"=")
{
// ToDo
}
else if (button.text == U"+")
{
expression += button.text;
}
else if (button.text == U"C")
{
// Clear the expression
expression.clear();
}
else
{
// Add the button text to the expression
expression += button.text;
}
}
}
// Draw the result display area
displayRect.draw(displayColor);
// Draw the expression in the result display area
font(expression).draw(36, Arg::rightCenter = (displayRect.rightCenter() + Vec2{ -10, 0 }), displayTextColor);
}
}
10. Add calculation processing¶

- Implement the
PushEqualfunction to calculate the input expression when the "=" button is pressed and make the result the new input expression. - Implement the
PushPlusfunction to add "+" to the input expression when the "+" button is pressed.
New Features
- The
!(not) operator inverts abooltype value. - The
.isEmpty()member function of aStringtype variable checks if the string is empty. - The
.back()member function of aStringtype variable returns the last character of the string. It causes an error if empty. - The
IsDigit(char32 ch)function checks if the argument characterchis a digit. - The
Eval(String expression)function evaluates the expressionexpressionand returns the result as adoubletype. ReturnsNaNif the expression is invalid.- You can check if it's
NaNwith theIsNaN(double x)function.
- You can check if it's
- The
Format(double x)function converts thedoubletype valuexto aStringand returns it.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
};
/// @brief Returns whether the expression ends with a digit.
/// @param expression The expression
/// @return true if it ends with a digit, false otherwise
bool EndsWithDigit(String expression)
{
if (expression.isEmpty())
{
return false;
}
return IsDigit(expression.back());
}
/// @brief Processes when the = button is pressed.
/// @param expression The current expression
/// @return The new expression
String PushEqual(String expression)
{
// Do nothing if it doesn't end with a digit
if (!EndsWithDigit(expression))
{
return expression;
}
// Evaluate the expression with the expression parser
double result = Eval(expression);
// Convert the evaluation result to a string and return it
return Format(result);
}
/// @brief Processes when the + button is pressed.
/// @param expression The current expression
/// @return The new expression
String PushPlus(String expression)
{
// Do nothing if it doesn't end with a digit
if (!EndsWithDigit(expression))
{
return expression;
}
// Add + to the end of the expression and return it
return (expression + U"+");
}
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Result display area color
const ColorF displayColor{ 0.8, 0.9, 0.8 };
// Result display area text color
const ColorF displayTextColor{ 0.1 };
// Result display area rectangle
const Rect displayRect{ 100, 100, 390, 90 };
// Array of button information
Array<Button> buttons;
buttons << Button{ Rect{ 100, 200, 90, 90 }, U"1" };
buttons << Button{ Rect{ 200, 200, 90, 90 }, U"2" };
buttons << Button{ Rect{ 300, 200, 90, 90 }, U"3" };
buttons << Button{ Rect{ 400, 200, 90, 90 }, U"+" };
buttons << Button{ Rect{ 400, 300, 90, 90 }, U"=" };
buttons << Button{ Rect{ 400, 400, 90, 90 }, U"C" };
// Input expression
String expression;
while (System::Update())
{
// For each button
for (const auto& button : buttons)
{
// Draw the button background
button.rect.rounded(8).draw();
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), ColorF{ 0.1 });
// When the mouse is over the button
if (button.rect.mouseOver())
{
// Change the mouse cursor to a hand
Cursor::RequestStyle(CursorStyle::Hand);
}
// When the button is clicked
if (button.rect.leftClicked())
{
if (button.text == U"=")
{
expression = PushEqual(expression);
}
else if (button.text == U"+")
{
expression = PushPlus(expression);
}
else if (button.text == U"C")
{
// Clear the expression
expression.clear();
}
else
{
// Add the button text to the expression
expression += button.text;
}
}
}
// Draw the result display area
displayRect.draw(displayColor);
// Draw the expression in the result display area
font(expression).draw(36, Arg::rightCenter = (displayRect.rightCenter() + Vec2{ -10, 0 }), displayTextColor);
}
}
11. Customize button styles¶

- Add
ColorFtype member variables to theButtonclass to represent the button's background color and text color.
Code
# include <Siv3D.hpp>
/// @brief Button class
struct Button
{
/// @brief Button rectangle
Rect rect;
/// @brief Button text
String text;
/// @brief Button color
ColorF backgroundColor;
/// @brief Text color
ColorF textColor;
};
/// @brief Returns whether the expression ends with a digit.
/// @param expression The expression
/// @return true if it ends with a digit, false otherwise
bool EndsWithDigit(String expression)
{
if (expression.isEmpty())
{
return false;
}
return IsDigit(expression.back());
}
/// @brief Processes when the = button is pressed.
/// @param expression The current expression
/// @return The new expression
String PushEqual(String expression)
{
// Do nothing if it doesn't end with a digit
if (!EndsWithDigit(expression))
{
return expression;
}
// Evaluate the expression with the expression parser
double result = Eval(expression);
// Convert the evaluation result to a string and return it
return Format(result);
}
/// @brief Processes when the + button is pressed.
/// @param expression The current expression
/// @return The new expression
String PushPlus(String expression)
{
// Do nothing if it doesn't end with a digit
if (!EndsWithDigit(expression))
{
return expression;
}
// Add + to the end of the expression and return it
return (expression + U"+");
}
void Main()
{
// Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// Font for buttons
const Font font{ FontMethod::MSDF, 40, Typeface::Bold };
// Number button color
const ColorF numberButtonColor{ 0.9, 0.95, 1.0 };
// Clear button color
const ColorF clearButtonColor{ 1.0, 0.8, 0.8 };
// Button text color
const ColorF buttonTextColor{ 0.1 };
// Result display area color
const ColorF displayColor{ 0.8, 0.9, 0.8 };
// Result display area text color
const ColorF displayTextColor{ 0.1 };
// Result display area rectangle
const Rect displayRect{ 100, 100, 390, 90 };
// Array of button information
Array<Button> buttons;
buttons << Button{ Rect{ 100, 200, 90, 90 }, U"1", numberButtonColor, buttonTextColor };
buttons << Button{ Rect{ 200, 200, 90, 90 }, U"2", numberButtonColor, buttonTextColor };
buttons << Button{ Rect{ 300, 200, 90, 90 }, U"3", numberButtonColor, buttonTextColor };
buttons << Button{ Rect{ 400, 200, 90, 90 }, U"+", numberButtonColor, buttonTextColor };
buttons << Button{ Rect{ 400, 300, 90, 90 }, U"=", numberButtonColor, buttonTextColor };
buttons << Button{ Rect{ 400, 400, 90, 90 }, U"C", clearButtonColor, buttonTextColor };
// Input expression
String expression;
while (System::Update())
{
// For each button
for (const auto& button : buttons)
{
// Draw the button background
button.rect.rounded(8).draw(button.backgroundColor);
// Draw the button text
font(button.text).drawAt(32, button.rect.center(), button.textColor);
// When the mouse is over the button
if (button.rect.mouseOver())
{
// Change the mouse cursor to a hand
Cursor::RequestStyle(CursorStyle::Hand);
}
// When the button is clicked
if (button.rect.leftClicked())
{
if (button.text == U"=")
{
expression = PushEqual(expression);
}
else if (button.text == U"+")
{
expression = PushPlus(expression);
}
else if (button.text == U"C")
{
// Clear the expression
expression.clear();
}
else
{
// Add the button text to the expression
expression += button.text;
}
}
}
// Draw the result display area
displayRect.draw(displayColor);
// Draw the expression in the result display area
font(expression).draw(36, Arg::rightCenter = (displayRect.rightCenter() + Vec2{ -10, 0 }), displayTextColor);
}
}
Advanced Features¶
From here on, try improving the program by thinking for yourself.
Feature Ideas¶
- Add the remaining numbers
- Add subtraction, multiplication, and division
Eval()also supports-,*, and/.
- Enable input of parentheses
- Add a backspace (delete one character) button
- The
.pop_back()member function ofStringdeletes the last character of the string. It causes an error if the string is empty.
- The
- Enable input of decimal numbers
- Enable number input with the keyboard
- Enable calculation of square roots
Eval()also supports expressions likesqrt(25).
- Record and reuse previous calculation results
- Copy the expression to the clipboard when clicking the display area
Clipboard::SetText(s);copies the stringsto the clipboard.
Design Ideas¶
- Arrange the size and layout of buttons
- Change button colors on mouse hover