Serenity Operating System
at portability 275 lines 10 kB view raw
1/* 2 * Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "CalculatorWidget.h" 28#include <AK/Assertions.h> 29#include <LibGUI/Button.h> 30#include <LibGUI/Label.h> 31#include <LibGUI/TextBox.h> 32 33CalculatorWidget::CalculatorWidget() 34{ 35 set_fill_with_background_color(true); 36 37 m_entry = add<GUI::TextBox>(); 38 m_entry->set_relative_rect(5, 5, 244, 26); 39 m_entry->set_text_alignment(Gfx::TextAlignment::CenterRight); 40 41 m_label = add<GUI::Label>(); 42 m_label->set_relative_rect(12, 42, 27, 27); 43 m_label->set_foreground_color(Color::NamedColor::Red); 44 m_label->set_frame_shadow(Gfx::FrameShadow::Sunken); 45 m_label->set_frame_shape(Gfx::FrameShape::Container); 46 m_label->set_frame_thickness(2); 47 48 update_display(); 49 50 for (int i = 0; i < 10; i++) { 51 m_digit_button[i] = add<GUI::Button>(); 52 auto& button = *m_digit_button[i]; 53 int p = i ? i + 2 : 0; 54 int x = 55 + (p % 3) * 39; 55 int y = 177 - (p / 3) * 33; 56 button.move_to(x, y); 57 button.set_foreground_color(Color::NamedColor::Blue); 58 add_button(button, i); 59 } 60 61 m_mem_add_button = add<GUI::Button>(); 62 m_mem_add_button->move_to(9, 177); 63 m_mem_add_button->set_foreground_color(Color::NamedColor::Red); 64 m_mem_add_button->set_text("M+"); 65 add_button(*m_mem_add_button, Calculator::Operation::MemAdd); 66 67 m_mem_save_button = add<GUI::Button>(); 68 m_mem_save_button->move_to(9, 144); 69 m_mem_save_button->set_foreground_color(Color::NamedColor::Red); 70 m_mem_save_button->set_text("MS"); 71 add_button(*m_mem_save_button, Calculator::Operation::MemSave); 72 73 m_mem_recall_button = add<GUI::Button>(); 74 m_mem_recall_button->move_to(9, 111); 75 m_mem_recall_button->set_foreground_color(Color::NamedColor::Red); 76 m_mem_recall_button->set_text("MR"); 77 add_button(*m_mem_recall_button, Calculator::Operation::MemRecall); 78 79 m_mem_clear_button = add<GUI::Button>(); 80 m_mem_clear_button->move_to(9, 78); 81 m_mem_clear_button->set_foreground_color(Color::NamedColor::Red); 82 m_mem_clear_button->set_text("MC"); 83 add_button(*m_mem_clear_button, Calculator::Operation::MemClear); 84 85 m_clear_button = add<GUI::Button>(); 86 m_clear_button->set_foreground_color(Color::NamedColor::Red); 87 m_clear_button->set_text("C"); 88 m_clear_button->on_click = [this](GUI::Button&) { 89 m_keypad.set_value(0.0); 90 m_calculator.clear_operation(); 91 update_display(); 92 }; 93 add_button(*m_clear_button); 94 m_clear_button->set_relative_rect(187, 40, 60, 28); 95 96 m_clear_error_button = add<GUI::Button>(); 97 m_clear_error_button->set_foreground_color(Color::NamedColor::Red); 98 m_clear_error_button->set_text("CE"); 99 m_clear_error_button->on_click = [this](GUI::Button&) { 100 m_calculator.clear_error(); 101 update_display(); 102 }; 103 add_button(*m_clear_error_button); 104 m_clear_error_button->set_relative_rect(124, 40, 59, 28); 105 106 m_backspace_button = add<GUI::Button>(); 107 m_backspace_button->set_foreground_color(Color::NamedColor::Red); 108 m_backspace_button->set_text("Backspace"); 109 m_backspace_button->on_click = [this](GUI::Button&) { 110 m_keypad.type_backspace(); 111 update_display(); 112 }; 113 add_button(*m_backspace_button); 114 m_backspace_button->set_relative_rect(55, 40, 65, 28); 115 116 m_decimal_point_button = add<GUI::Button>(); 117 m_decimal_point_button->move_to(133, 177); 118 m_decimal_point_button->set_foreground_color(Color::NamedColor::Blue); 119 m_decimal_point_button->set_text("."); 120 m_decimal_point_button->on_click = [this](GUI::Button&) { 121 m_keypad.type_decimal_point(); 122 update_display(); 123 }; 124 add_button(*m_decimal_point_button); 125 126 m_sign_button = add<GUI::Button>(); 127 m_sign_button->move_to(94, 177); 128 m_sign_button->set_foreground_color(Color::NamedColor::Blue); 129 m_sign_button->set_text("+/-"); 130 add_button(*m_sign_button, Calculator::Operation::ToggleSign); 131 132 m_add_button = add<GUI::Button>(); 133 m_add_button->move_to(172, 177); 134 m_add_button->set_foreground_color(Color::NamedColor::Red); 135 m_add_button->set_text("+"); 136 add_button(*m_add_button, Calculator::Operation::Add); 137 138 m_subtract_button = add<GUI::Button>(); 139 m_subtract_button->move_to(172, 144); 140 m_subtract_button->set_foreground_color(Color::NamedColor::Red); 141 m_subtract_button->set_text("-"); 142 add_button(*m_subtract_button, Calculator::Operation::Subtract); 143 144 m_multiply_button = add<GUI::Button>(); 145 m_multiply_button->move_to(172, 111); 146 m_multiply_button->set_foreground_color(Color::NamedColor::Red); 147 m_multiply_button->set_text("*"); 148 add_button(*m_multiply_button, Calculator::Operation::Multiply); 149 150 m_divide_button = add<GUI::Button>(); 151 m_divide_button->move_to(172, 78); 152 m_divide_button->set_foreground_color(Color::NamedColor::Red); 153 m_divide_button->set_text("/"); 154 add_button(*m_divide_button, Calculator::Operation::Divide); 155 156 m_sqrt_button = add<GUI::Button>(); 157 m_sqrt_button->move_to(211, 78); 158 m_sqrt_button->set_foreground_color(Color::NamedColor::Blue); 159 m_sqrt_button->set_text("sqrt"); 160 add_button(*m_sqrt_button, Calculator::Operation::Sqrt); 161 162 m_inverse_button = add<GUI::Button>(); 163 m_inverse_button->move_to(211, 144); 164 m_inverse_button->set_foreground_color(Color::NamedColor::Blue); 165 m_inverse_button->set_text("1/x"); 166 add_button(*m_inverse_button, Calculator::Operation::Inverse); 167 168 m_percent_button = add<GUI::Button>(); 169 m_percent_button->move_to(211, 111); 170 m_percent_button->set_foreground_color(Color::NamedColor::Blue); 171 m_percent_button->set_text("%"); 172 add_button(*m_percent_button, Calculator::Operation::Percent); 173 174 m_equals_button = add<GUI::Button>(); 175 m_equals_button->move_to(211, 177); 176 m_equals_button->set_foreground_color(Color::NamedColor::Red); 177 m_equals_button->set_text("="); 178 m_equals_button->on_click = [this](GUI::Button&) { 179 double argument = m_keypad.value(); 180 double res = m_calculator.finish_operation(argument); 181 m_keypad.set_value(res); 182 update_display(); 183 }; 184 add_button(*m_equals_button); 185} 186 187CalculatorWidget::~CalculatorWidget() 188{ 189} 190 191void CalculatorWidget::add_button(GUI::Button& button, Calculator::Operation operation) 192{ 193 add_button(button); 194 button.on_click = [this, operation](GUI::Button&) { 195 double argument = m_keypad.value(); 196 double res = m_calculator.begin_operation(operation, argument); 197 m_keypad.set_value(res); 198 update_display(); 199 }; 200} 201 202void CalculatorWidget::add_button(GUI::Button& button, int digit) 203{ 204 add_button(button); 205 button.set_text(String::number(digit)); 206 button.on_click = [this, digit](GUI::Button&) { 207 m_keypad.type_digit(digit); 208 update_display(); 209 }; 210} 211 212void CalculatorWidget::add_button(GUI::Button& button) 213{ 214 button.resize(35, 28); 215} 216 217void CalculatorWidget::update_display() 218{ 219 m_entry->set_text(m_keypad.to_string()); 220 if (m_calculator.has_error()) 221 m_label->set_text("E"); 222 else 223 m_label->set_text(""); 224} 225 226void CalculatorWidget::keydown_event(GUI::KeyEvent& event) 227{ 228 //Clear button selection when we are typing 229 m_equals_button->set_focus(true); 230 m_equals_button->set_focus(false); 231 232 if (event.key() == KeyCode::Key_Return) { 233 m_keypad.set_value(m_calculator.finish_operation(m_keypad.value())); 234 235 } else if (event.key() >= KeyCode::Key_0 && event.key() <= KeyCode::Key_9) { 236 m_keypad.type_digit(atoi(event.text().characters())); 237 238 } else if (event.key() == KeyCode::Key_Period) { 239 m_keypad.type_decimal_point(); 240 241 } else if (event.key() == KeyCode::Key_Escape) { 242 m_keypad.set_value(0.0); 243 m_calculator.clear_operation(); 244 245 } else if (event.key() == KeyCode::Key_Backspace) { 246 m_keypad.type_backspace(); 247 248 } else { 249 Calculator::Operation operation; 250 251 switch (event.key()) { 252 case KeyCode::Key_Plus: 253 operation = Calculator::Operation::Add; 254 break; 255 case KeyCode::Key_Minus: 256 operation = Calculator::Operation::Subtract; 257 break; 258 case KeyCode::Key_Asterisk: 259 operation = Calculator::Operation::Multiply; 260 break; 261 case KeyCode::Key_Slash: 262 operation = Calculator::Operation::Divide; 263 break; 264 case KeyCode::Key_Percent: 265 operation = Calculator::Operation::Percent; 266 break; 267 default: 268 return; 269 } 270 271 m_keypad.set_value(m_calculator.begin_operation(operation, m_keypad.value())); 272 } 273 274 update_display(); 275}