From e3b22c395d7bef1897284f88ae0bacc77a8659a9 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Thu, 22 Sep 2022 16:55:04 +0200 Subject: [PATCH] Calculator: Add a "Custom" entry to the rounding menu This entry pop a dialog to ask the user to enter a value. The Calculator will automatically put itself in this mode if you enter a number with more digits in the fractional part than the actual maximum length. --- .../Applications/Calculator/CMakeLists.txt | 1 + .../Calculator/CalculatorWidget.cpp | 15 +++- .../Calculator/CalculatorWidget.h | 7 ++ Userland/Applications/Calculator/Keypad.cpp | 8 ++- Userland/Applications/Calculator/Keypad.h | 3 +- .../Calculator/RoundingDialog.cpp | 71 +++++++++++++++++++ .../Applications/Calculator/RoundingDialog.h | 29 ++++++++ Userland/Applications/Calculator/main.cpp | 33 +++++++-- 8 files changed, 157 insertions(+), 10 deletions(-) create mode 100644 Userland/Applications/Calculator/RoundingDialog.cpp create mode 100644 Userland/Applications/Calculator/RoundingDialog.h diff --git a/Userland/Applications/Calculator/CMakeLists.txt b/Userland/Applications/Calculator/CMakeLists.txt index 8584cd905bd..476ba67bb16 100644 --- a/Userland/Applications/Calculator/CMakeLists.txt +++ b/Userland/Applications/Calculator/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES main.cpp Calculator.cpp CalculatorWidget.cpp + RoundingDialog.cpp Keypad.cpp CalculatorGML.h ) diff --git a/Userland/Applications/Calculator/CalculatorWidget.cpp b/Userland/Applications/Calculator/CalculatorWidget.cpp index a2f1c53666c..10717da2f11 100644 --- a/Userland/Applications/Calculator/CalculatorWidget.cpp +++ b/Userland/Applications/Calculator/CalculatorWidget.cpp @@ -9,7 +9,6 @@ #include "CalculatorWidget.h" #include -#include #include #include #include @@ -160,8 +159,7 @@ void CalculatorWidget::keydown_event(GUI::KeyEvent& event) m_keypad.set_value(m_calculator.finish_operation(m_keypad.value())); mimic_pressed_button(m_equals_button); } else if (event.code_point() >= '0' && event.code_point() <= '9') { - u32 digit = event.code_point() - '0'; - m_keypad.type_digit(digit); + auto const digit = m_keypad.type_digit(event.code_point() - '0'); mimic_pressed_button(m_digit_button[digit]); } else if (event.code_point() == '.') { m_keypad.type_decimal_point(); @@ -219,8 +217,19 @@ void CalculatorWidget::keydown_event(GUI::KeyEvent& event) update_display(); } +unsigned CalculatorWidget::rounding_length() const +{ + return m_keypad.rounding_length(); +} + void CalculatorWidget::set_rounding_length(unsigned rounding_threshold) { m_keypad.set_rounding_length(rounding_threshold); update_display(); } + +void CalculatorWidget::set_rounding_custom(GUI::Action& action, StringView format) +{ + m_format = format; + m_rounding_custom = action; +} diff --git a/Userland/Applications/Calculator/CalculatorWidget.h b/Userland/Applications/Calculator/CalculatorWidget.h index 6415af4277d..b74242fc5f9 100644 --- a/Userland/Applications/Calculator/CalculatorWidget.h +++ b/Userland/Applications/Calculator/CalculatorWidget.h @@ -12,6 +12,7 @@ #include "Keypad.h" #include #include +#include #include class CalculatorWidget final : public GUI::Widget { @@ -21,8 +22,11 @@ public: String get_entry(); void set_entry(Crypto::BigFraction); + unsigned rounding_length() const; void set_rounding_length(unsigned); + void set_rounding_custom(GUI::Action& action, StringView); + private: CalculatorWidget(); void add_operation_button(GUI::Button&, Calculator::Operation); @@ -58,4 +62,7 @@ private: RefPtr m_inverse_button; RefPtr m_percent_button; RefPtr m_equals_button; + + StringView m_format; + RefPtr m_rounding_custom; }; diff --git a/Userland/Applications/Calculator/Keypad.cpp b/Userland/Applications/Calculator/Keypad.cpp index e1624d7a0da..55b685e6fd4 100644 --- a/Userland/Applications/Calculator/Keypad.cpp +++ b/Userland/Applications/Calculator/Keypad.cpp @@ -12,7 +12,7 @@ #include #include -void Keypad::type_digit(int digit) +unsigned Keypad::type_digit(int digit) { switch (m_state) { case State::External: @@ -34,6 +34,7 @@ void Keypad::type_digit(int digit) m_frac_length.set_to(m_frac_length.plus(1)); break; } + return m_frac_length.to_u64(); } void Keypad::type_decimal_point() @@ -139,3 +140,8 @@ void Keypad::set_rounding_length(unsigned rounding_threshold) { m_displayed_fraction_length = rounding_threshold; } + +unsigned Keypad::rounding_length() const +{ + return m_displayed_fraction_length; +} diff --git a/Userland/Applications/Calculator/Keypad.h b/Userland/Applications/Calculator/Keypad.h index b71b9214da1..b658f5e5406 100644 --- a/Userland/Applications/Calculator/Keypad.h +++ b/Userland/Applications/Calculator/Keypad.h @@ -21,7 +21,7 @@ public: Keypad() = default; ~Keypad() = default; - void type_digit(int digit); + unsigned type_digit(int digit); void type_decimal_point(); void type_backspace(); @@ -30,6 +30,7 @@ public: void set_to_0(); void set_rounding_length(unsigned); + unsigned rounding_length() const; String to_string() const; diff --git a/Userland/Applications/Calculator/RoundingDialog.cpp b/Userland/Applications/Calculator/RoundingDialog.cpp new file mode 100644 index 00000000000..b968b42cd74 --- /dev/null +++ b/Userland/Applications/Calculator/RoundingDialog.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022, Lucas Chollet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "RoundingDialog.h" +#include +#include +#include +#include +#include + +RoundingDialog::ExecResult RoundingDialog::show(GUI::Window* parent_window, unsigned& rounding_value) +{ + auto dialog = RoundingDialog::construct(parent_window); + + if (parent_window) { + dialog->set_icon(parent_window->icon()); + dialog->center_within(*parent_window); + } + + dialog->m_rounding_spinbox->set_value(rounding_value); + + auto const result = dialog->exec(); + + if (result != GUI::Dialog::ExecResult::OK) + return result; + + rounding_value = dialog->m_rounding_spinbox->value(); + + return GUI::Dialog::ExecResult::OK; +} + +RoundingDialog::RoundingDialog(GUI::Window* parent_window) + : Dialog(parent_window) +{ + resize(m_dialog_length, m_dialog_height); + set_resizable(false); + set_title("Choose custom rounding"); + + auto& main_widget = set_main_widget(); + + main_widget.set_fill_with_background_color(true); + main_widget.set_layout(); + + m_rounding_spinbox = GUI::SpinBox::construct(); + m_buttons_container = GUI::Widget::construct(); + m_ok_button = GUI::DialogButton::construct("OK"); + m_cancel_button = GUI::DialogButton::construct("Cancel"); + + main_widget.add_child(*m_rounding_spinbox); + main_widget.add_child(*m_buttons_container); + + m_buttons_container->set_layout(); + m_buttons_container->layout()->add_spacer(); + m_buttons_container->add_child(*m_ok_button); + m_buttons_container->add_child(*m_cancel_button); + + m_rounding_spinbox->on_return_pressed = [this] { + m_ok_button->click(); + }; + + m_ok_button->on_click = [this](auto) { + done(ExecResult::OK); + }; + + m_cancel_button->on_click = [this](auto) { + done(ExecResult::Cancel); + }; +} diff --git a/Userland/Applications/Calculator/RoundingDialog.h b/Userland/Applications/Calculator/RoundingDialog.h new file mode 100644 index 00000000000..24fa8990c35 --- /dev/null +++ b/Userland/Applications/Calculator/RoundingDialog.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022, Lucas Chollet + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +class RoundingDialog : public GUI::Dialog { + C_OBJECT(RoundingDialog); + +public: + static ExecResult show(GUI::Window* parent_window, unsigned& rounding_value); + +private: + RoundingDialog(GUI::Window* parent_window); + virtual ~RoundingDialog() override = default; + + RefPtr m_rounding_spinbox; + RefPtr m_buttons_container; + RefPtr m_ok_button; + RefPtr m_cancel_button; + + static constexpr unsigned m_dialog_length = 200; + static constexpr unsigned m_dialog_height = 54; +}; diff --git a/Userland/Applications/Calculator/main.cpp b/Userland/Applications/Calculator/main.cpp index c2076ccaad3..3a8750b3381 100644 --- a/Userland/Applications/Calculator/main.cpp +++ b/Userland/Applications/Calculator/main.cpp @@ -5,6 +5,7 @@ */ #include "CalculatorWidget.h" +#include "RoundingDialog.h" #include #include #include @@ -51,7 +52,8 @@ ErrorOr serenity_main(Main::Arguments arguments) auto clipboard = GUI::Clipboard::the().fetch_data_and_type(); if (clipboard.mime_type == "text/plain") { if (!clipboard.data.is_empty()) { - widget->set_entry(Crypto::BigFraction(StringView(clipboard.data))); + auto const number = StringView(clipboard.data); + widget->set_entry(Crypto::BigFraction(number)); } } })); @@ -74,16 +76,37 @@ ErrorOr serenity_main(Main::Arguments arguments) static constexpr auto rounding_modes = Array { 0, 2, 4 }; - for (auto const rounding_mode : rounding_modes) { - auto round_action = GUI::Action::create_checkable(String::formatted("To &{} digits", rounding_mode), [&widget, rounding_mode](auto&) { - widget->set_rounding_length(rounding_mode); - }); + Optional last_rounding_mode = 1; + for (unsigned i {}; i < rounding_modes.size(); ++i) { + auto round_action = GUI::Action::create_checkable(String::formatted("To &{} digits", rounding_modes[i]), + [&widget, rounding_mode = rounding_modes[i], &last_rounding_mode, i](auto&) { + widget->set_rounding_length(rounding_mode); + last_rounding_mode = i; + }); preview_actions.add_action(*round_action); round_menu.add_action(*round_action); } + constexpr auto format { "&Custom - {} ..."sv }; + auto round_custom = GUI::Action::create_checkable(String::formatted(format, 0), [&](auto& action) { + unsigned custom_rounding_length = widget->rounding_length(); + + if (RoundingDialog::show(window, custom_rounding_length) == GUI::Dialog::ExecResult::OK) { + action.set_text(String::formatted(format, custom_rounding_length)); + widget->set_rounding_length(custom_rounding_length); + last_rounding_mode.clear(); + } else if (last_rounding_mode.has_value()) + round_menu.action_at(last_rounding_mode.value())->activate(); + }); + + widget->set_rounding_custom(round_custom, format); + + preview_actions.add_action(*round_custom); preview_actions.set_exclusive(true); + round_menu.add_action(*round_custom); + + round_menu.action_at(last_rounding_mode.value())->activate(); auto& help_menu = window->add_menu("&Help"); help_menu.add_action(GUI::CommonActions::make_about_action("Calculator", app_icon, window));