summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarol Baraniecki <karol@baraniecki.eu>2022-12-26 17:12:46 +0100
committerAndreas Kling <kling@serenityos.org>2022-12-31 00:07:13 +0100
commit21cc8f65f5c28a39843e1fa94ae819db74b1e5c8 (patch)
treeced80bd579134dc2392f72dcd83cb81d47590ec4
parentef9fd6c2863fb393b422675d8121293c98e44f51 (diff)
downloadserenity-21cc8f65f5c28a39843e1fa94ae819db74b1e5c8.zip
Calculator: Support chaining and repeating operations
The calculator now supports chaining (hitting "1+2+3=" shows "6" instead of "5") and repeating ("2+2===" shows "8") operations. :^)
-rw-r--r--Userland/Applications/Calculator/Calculator.cpp103
-rw-r--r--Userland/Applications/Calculator/Calculator.h21
-rw-r--r--Userland/Applications/Calculator/CalculatorWidget.cpp67
-rw-r--r--Userland/Applications/Calculator/Keypad.cpp5
-rw-r--r--Userland/Applications/Calculator/Keypad.h2
5 files changed, 126 insertions, 72 deletions
diff --git a/Userland/Applications/Calculator/Calculator.cpp b/Userland/Applications/Calculator/Calculator.cpp
index 451aa5ea01..9e68d1882a 100644
--- a/Userland/Applications/Calculator/Calculator.cpp
+++ b/Userland/Applications/Calculator/Calculator.cpp
@@ -10,9 +10,13 @@
#include <AK/Math.h>
#include <LibCrypto/BigFraction/BigFraction.h>
-Crypto::BigFraction Calculator::begin_operation(Operation operation, Crypto::BigFraction argument)
+Optional<Crypto::BigFraction> Calculator::operation_with_literal_argument(Operation operation, Crypto::BigFraction argument)
{
- Crypto::BigFraction res {};
+ // If a previous operation is still in progress, finish it
+ // Makes hitting "1+2+3=" equivalent to hitting "1+2=+3="
+ if (m_binary_operation_in_progress != Operation::None) {
+ argument = finish_binary_operation(m_binary_operation_saved_left_side, m_binary_operation_in_progress, argument);
+ }
switch (operation) {
case Operation::None:
@@ -22,78 +26,117 @@ Crypto::BigFraction Calculator::begin_operation(Operation operation, Crypto::Big
case Operation::Subtract:
case Operation::Multiply:
case Operation::Divide:
- m_saved_argument = argument;
- m_operation_in_progress = operation;
- return argument;
+ m_binary_operation_saved_left_side = argument;
+ m_binary_operation_in_progress = operation;
+ m_current_value = argument;
+ break;
case Operation::Sqrt:
if (argument < Crypto::BigFraction {}) {
m_has_error = true;
- return argument;
+ m_current_value = argument;
+ break;
}
- res = argument.sqrt();
+ m_current_value = argument.sqrt();
clear_operation();
break;
case Operation::Inverse:
if (argument == Crypto::BigFraction {}) {
m_has_error = true;
- return argument;
+ m_current_value = argument;
+ break;
}
- res = argument.invert();
+ m_current_value = argument.invert();
clear_operation();
break;
case Operation::Percent:
- res = argument * Crypto::BigFraction { 1, 100 };
+ m_current_value = argument * Crypto::BigFraction { 1, 100 };
break;
case Operation::ToggleSign:
- res = -argument;
+ m_current_value = -argument;
break;
case Operation::MemClear:
m_mem.set_to_0();
- res = argument;
+ m_current_value = argument;
break;
case Operation::MemRecall:
- res = m_mem;
+ m_current_value = m_mem;
break;
case Operation::MemSave:
m_mem = argument;
- res = argument;
+ m_current_value = argument;
break;
case Operation::MemAdd:
m_mem = m_mem + argument; // avoids the need for operator+=()
- res = m_mem;
+ m_current_value = m_mem;
+ break;
+ case Operation::Equals:
+ m_current_value = argument;
break;
}
- return res;
+ return m_current_value;
+}
+
+static bool operation_is_binary(Calculator::Operation operation)
+{
+ switch (operation) {
+
+ case Calculator::Operation::Add:
+ case Calculator::Operation::Subtract:
+ case Calculator::Operation::Multiply:
+ case Calculator::Operation::Divide:
+ return true;
+
+ default:
+ return false;
+ }
}
-Crypto::BigFraction Calculator::finish_operation(Crypto::BigFraction argument)
+Optional<Crypto::BigFraction> Calculator::operation_without_argument(Operation operation)
+{
+ bool in_binary_operation = m_binary_operation_in_progress != Operation::None;
+ bool entering_new_binary_operation = operation_is_binary(operation);
+ bool previous_operation_was_binary = operation_is_binary(m_previous_operation);
+
+ if (in_binary_operation && entering_new_binary_operation) {
+ m_binary_operation_in_progress = operation;
+ return {};
+ }
+ if (!in_binary_operation && previous_operation_was_binary && operation == Operation::Equals) {
+ m_current_value = finish_binary_operation(m_current_value, m_previous_operation, m_previous_binary_operation_right_side);
+ return m_current_value;
+ }
+ return operation_with_literal_argument(operation, m_current_value);
+}
+
+Crypto::BigFraction Calculator::finish_binary_operation(Crypto::BigFraction const& left_side, Operation operation, Crypto::BigFraction const& right_side)
{
Crypto::BigFraction res {};
- switch (m_operation_in_progress) {
- case Operation::None:
- return argument;
+ m_previous_binary_operation_right_side = right_side;
+
+ switch (operation) {
case Operation::Add:
- res = m_saved_argument + argument;
+ res = left_side + right_side;
break;
case Operation::Subtract:
- res = m_saved_argument - argument;
+ res = left_side - right_side;
break;
case Operation::Multiply:
- res = m_saved_argument * argument;
+ res = left_side * right_side;
break;
case Operation::Divide:
- if (argument == Crypto::BigFraction {}) {
+ if (right_side == Crypto::BigFraction {}) {
m_has_error = true;
- return argument;
+ } else {
+ res = left_side / right_side;
}
- res = m_saved_argument / argument;
break;
+ case Operation::None:
case Operation::Sqrt:
case Operation::Inverse:
case Operation::Percent:
@@ -102,6 +145,7 @@ Crypto::BigFraction Calculator::finish_operation(Crypto::BigFraction argument)
case Operation::MemRecall:
case Operation::MemSave:
case Operation::MemAdd:
+ case Operation::Equals:
VERIFY_NOT_REACHED();
}
@@ -111,7 +155,10 @@ Crypto::BigFraction Calculator::finish_operation(Crypto::BigFraction argument)
void Calculator::clear_operation()
{
- m_operation_in_progress = Operation::None;
- m_saved_argument.set_to_0();
+ if (m_binary_operation_in_progress != Operation::None) {
+ m_previous_operation = m_binary_operation_in_progress;
+ m_binary_operation_in_progress = Operation::None;
+ }
+ m_binary_operation_saved_left_side.set_to_0();
clear_error();
}
diff --git a/Userland/Applications/Calculator/Calculator.h b/Userland/Applications/Calculator/Calculator.h
index 5d995bf7e1..7d249a4539 100644
--- a/Userland/Applications/Calculator/Calculator.h
+++ b/Userland/Applications/Calculator/Calculator.h
@@ -7,6 +7,7 @@
#pragma once
+#include <AK/Optional.h>
#include <LibCrypto/BigFraction/BigFraction.h>
// This type implements the regular calculator
@@ -36,11 +37,13 @@ public:
MemClear,
MemRecall,
MemSave,
- MemAdd
+ MemAdd,
+
+ Equals
};
- Crypto::BigFraction begin_operation(Operation, Crypto::BigFraction);
- Crypto::BigFraction finish_operation(Crypto::BigFraction);
+ Optional<Crypto::BigFraction> operation_with_literal_argument(Operation, Crypto::BigFraction);
+ Optional<Crypto::BigFraction> operation_without_argument(Operation);
bool has_error() const { return m_has_error; }
@@ -48,8 +51,16 @@ public:
void clear_error() { m_has_error = false; }
private:
- Operation m_operation_in_progress { Operation::None };
- Crypto::BigFraction m_saved_argument {};
Crypto::BigFraction m_mem {};
+
+ Crypto::BigFraction m_current_value {};
+
+ Operation m_binary_operation_in_progress { Operation::None };
+ Crypto::BigFraction m_binary_operation_saved_left_side {};
+
+ Operation m_previous_operation { Operation::None };
+ Crypto::BigFraction m_previous_binary_operation_right_side {};
bool m_has_error { false };
+
+ Crypto::BigFraction finish_binary_operation(Crypto::BigFraction const& left_side, Operation operation, Crypto::BigFraction const& right_side);
};
diff --git a/Userland/Applications/Calculator/CalculatorWidget.cpp b/Userland/Applications/Calculator/CalculatorWidget.cpp
index c1129e5e07..a6615211a4 100644
--- a/Userland/Applications/Calculator/CalculatorWidget.cpp
+++ b/Userland/Applications/Calculator/CalculatorWidget.cpp
@@ -97,19 +97,22 @@ CalculatorWidget::CalculatorWidget()
add_operation_button(*m_percent_button, Calculator::Operation::Percent);
m_equals_button = *find_descendant_of_type_named<GUI::Button>("equal_button");
- m_equals_button->on_click = [this](auto) {
- Crypto::BigFraction argument = m_keypad.value();
- Crypto::BigFraction res = m_calculator.finish_operation(move(argument));
- m_keypad.set_value(move(res));
- update_display();
- };
+ add_operation_button(*m_equals_button, Calculator::Operation::Equals);
}
void CalculatorWidget::perform_operation(Calculator::Operation operation)
{
- Crypto::BigFraction argument = m_keypad.value();
- Crypto::BigFraction res = m_calculator.begin_operation(operation, move(argument));
- m_keypad.set_value(move(res));
+ Optional<Crypto::BigFraction> res;
+ if (m_keypad.in_typing_state()) {
+ Crypto::BigFraction argument = m_keypad.value();
+ res = m_calculator.operation_with_literal_argument(operation, move(argument));
+ } else {
+ res = m_calculator.operation_without_argument(operation);
+ }
+
+ if (res.has_value()) {
+ m_keypad.set_value(move(res.value()));
+ }
update_display();
}
@@ -156,7 +159,7 @@ void CalculatorWidget::update_display()
void CalculatorWidget::keydown_event(GUI::KeyEvent& event)
{
if (event.key() == KeyCode::Key_Return || event.key() == KeyCode::Key_Equal) {
- m_keypad.set_value(m_calculator.finish_operation(m_keypad.value()));
+ perform_operation(Calculator::Operation::Equals);
mimic_pressed_button(m_equals_button);
} else if (event.code_point() >= '0' && event.code_point() <= '9') {
auto const digit = event.code_point() - '0';
@@ -184,35 +187,21 @@ void CalculatorWidget::keydown_event(GUI::KeyEvent& event)
} else if (event.key() == KeyCode::Key_I) {
perform_operation(Calculator::Operation::Inverse);
mimic_pressed_button(m_inverse_button);
- } else {
- Calculator::Operation operation;
-
- switch (event.code_point()) {
- case '+':
- operation = Calculator::Operation::Add;
- mimic_pressed_button(m_add_button);
- break;
- case '-':
- operation = Calculator::Operation::Subtract;
- mimic_pressed_button(m_subtract_button);
- break;
- case '*':
- operation = Calculator::Operation::Multiply;
- mimic_pressed_button(m_multiply_button);
- break;
- case '/':
- operation = Calculator::Operation::Divide;
- mimic_pressed_button(m_divide_button);
- break;
- case '%':
- operation = Calculator::Operation::Percent;
- mimic_pressed_button(m_percent_button);
- break;
- default:
- return;
- }
-
- m_keypad.set_value(m_calculator.begin_operation(operation, m_keypad.value()));
+ } else if (event.code_point() == '+') {
+ perform_operation(Calculator::Operation::Add);
+ mimic_pressed_button(m_add_button);
+ } else if (event.code_point() == '-') {
+ perform_operation(Calculator::Operation::Subtract);
+ mimic_pressed_button(m_subtract_button);
+ } else if (event.code_point() == '*') {
+ perform_operation(Calculator::Operation::Multiply);
+ mimic_pressed_button(m_multiply_button);
+ } else if (event.code_point() == '/') {
+ perform_operation(Calculator::Operation::Divide);
+ mimic_pressed_button(m_divide_button);
+ } else if (event.code_point() == '%') {
+ perform_operation(Calculator::Operation::Percent);
+ mimic_pressed_button(m_percent_button);
}
update_display();
diff --git a/Userland/Applications/Calculator/Keypad.cpp b/Userland/Applications/Calculator/Keypad.cpp
index c116c02580..66c911a771 100644
--- a/Userland/Applications/Calculator/Keypad.cpp
+++ b/Userland/Applications/Calculator/Keypad.cpp
@@ -135,6 +135,11 @@ DeprecatedString Keypad::to_deprecated_string() const
return builder.to_deprecated_string();
}
+bool Keypad::in_typing_state() const
+{
+ return m_state == State::TypingDecimal || m_state == State::TypingInteger;
+}
+
void Keypad::set_rounding_length(unsigned rounding_threshold)
{
m_displayed_fraction_length = rounding_threshold;
diff --git a/Userland/Applications/Calculator/Keypad.h b/Userland/Applications/Calculator/Keypad.h
index 62a993a76d..af32de260f 100644
--- a/Userland/Applications/Calculator/Keypad.h
+++ b/Userland/Applications/Calculator/Keypad.h
@@ -35,6 +35,8 @@ public:
DeprecatedString to_deprecated_string() const;
+ bool in_typing_state() const;
+
private:
// Internal representation of the current decimal value.
// Those variables are only used when the user is entering a value.