Commit d81b3a62 authored by Rohan Asokan's avatar Rohan Asokan
Browse files

Added 3 different output formats for binary calculator

Updated MathEngine to handle KNumber
parent b0d07eb8
/*
* SPDX-FileCopyrightText: 2020-2021 Han Young <hanyoung@protonmail.com>
* SPDX-FileCopyrightText: 2021-2022 Rohan Asokan
* <rohan.asokan@students.iiit.ac.in>
* SPDX-FileCopyrightText: 2021-2022 Rohan Asokan <rohan.asokan@students.iiit.ac.in>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "inputmanager.h"
#include "historymanager.h"
#include "mathengine.h"
#include "historymanager.h"
#include <QDebug>
InputManager::InputManager()
{
......@@ -33,11 +32,23 @@ const QString &InputManager::result() const
return m_result;
}
const QString &InputManager::binaryResult() const
{
return m_binaryResult;
}
const QString &InputManager::hexResult() const
{
return m_hexResult;
}
void InputManager::append(const QString &subexpression)
{
// if expression was from result and input is numeric, clear expression
if (m_moveFromResult && subexpression.size() == 1) {
if (subexpression.at(0).isDigit() || subexpression.at(0) == QLatin1Char('.')) {
if(m_moveFromResult && subexpression.size() == 1)
{
if(subexpression.at(0).isDigit() || subexpression.at(0) == QLatin1Char('.'))
{
m_expression.clear();
m_stack.pop_back();
}
......@@ -45,39 +56,52 @@ void InputManager::append(const QString &subexpression)
m_moveFromResult = false;
// Call the corresponding parser based on the type of expression.
MathEngine *engineInstance = MathEngine::inst();
MathEngine * engineInstance = MathEngine::inst();
if (m_isBinaryMode) {
engineInstance->parseBinaryExpression(m_expression + subexpression);
} else {
engineInstance->parse(m_expression + subexpression);
}
if (!MathEngine::inst()->error()) {
if(!MathEngine::inst()->error())
{
m_stack.push_back(subexpression.size());
m_result = MathEngine::inst()->result();
KNumber result = MathEngine::inst()->result();
m_result = result.toQString();
m_binaryResult = result.toBinaryString(0);
m_hexResult = result.toHexString(0);
m_expression += subexpression;
Q_EMIT resultChanged();
Q_EMIT binaryResultChanged();
Q_EMIT hexResultChanged();
Q_EMIT expressionChanged();
}
}
void InputManager::backspace()
{
if (!m_stack.empty()) {
if(!m_stack.empty())
{
m_expression.chop(m_stack.back());
Q_EMIT expressionChanged();
// Call the corresponding parser based on the type of expression.
MathEngine *engineInstance = MathEngine::inst();
MathEngine * engineInstance = MathEngine::inst();
if (m_isBinaryMode) {
engineInstance->parseBinaryExpression(m_expression);
} else {
engineInstance->parse(m_expression);
}
if (!MathEngine::inst()->error()) {
m_result = MathEngine::inst()->result();
if(!MathEngine::inst()->error())
{
KNumber result = MathEngine::inst()->result();
m_result = result.toQString();
m_binaryResult = result.toBinaryString(0);
m_hexResult = result.toHexString(0);
Q_EMIT resultChanged();
Q_EMIT binaryResultChanged();
Q_EMIT hexResultChanged();
}
}
}
......@@ -87,40 +111,48 @@ void InputManager::equal()
HistoryManager::inst()->addHistory(m_expression + QStringLiteral(" = ") + m_result);
m_expression = m_result;
m_result.clear();
m_binaryResult.clear();
m_hexResult.clear();
m_stack.clear();
m_stack.push_back(m_result.size());
m_moveFromResult = true;
Q_EMIT expressionChanged();
Q_EMIT resultChanged();
Q_EMIT binaryResultChanged();
Q_EMIT hexResultChanged();
}
void InputManager::clear()
{
m_expression.clear();
m_result.clear();
m_binaryResult.clear();
m_hexResult.clear();
m_stack.clear();
Q_EMIT expressionChanged();
Q_EMIT resultChanged();
Q_EMIT binaryResultChanged();
Q_EMIT hexResultChanged();
}
void InputManager::fromHistory(const QString &result)
{
setExpression(result);
m_expression = result;
m_result.clear();
m_binaryResult.clear();
m_hexResult.clear();
m_stack.clear();
m_stack.push_back(result.size());
m_moveFromResult = true;
Q_EMIT expressionChanged();
Q_EMIT resultChanged();
Q_EMIT binaryResultChanged();
Q_EMIT hexResultChanged();
}
bool InputManager::binaryMode() const
{
return m_isBinaryMode;
}
void InputManager::setBinaryMode(bool active)
{
void InputManager::setBinaryMode(bool active) {
m_isBinaryMode = active;
clear();
}
......@@ -13,27 +13,34 @@ class InputManager : public QObject
Q_OBJECT
Q_PROPERTY(QString expression READ expression WRITE setExpression NOTIFY expressionChanged)
Q_PROPERTY(QString result READ result NOTIFY resultChanged)
Q_PROPERTY(QString binaryResult READ binaryResult NOTIFY binaryResultChanged)
Q_PROPERTY(QString hexResult READ hexResult NOTIFY hexResultChanged)
public:
static InputManager *inst();
const QString &expression() const;
void setExpression(const QString &expression);
const QString &result() const;
const QString &binaryResult() const;
const QString &hexResult() const;
Q_INVOKABLE void append(const QString &subexpression);
Q_INVOKABLE void backspace();
Q_INVOKABLE void equal();
Q_INVOKABLE void clear();
Q_INVOKABLE void fromHistory(const QString &result);
Q_INVOKABLE bool binaryMode() const;
Q_INVOKABLE void setBinaryMode(bool active);
Q_SIGNALS:
void expressionChanged();
void resultChanged();
void binaryResultChanged();
void hexResultChanged();
private:
InputManager();
bool m_moveFromResult = false; // clear expression on none operator input
std::vector<int> m_stack; // track subexpression length for removal later
QString m_expression;
QString m_result;
QString m_binaryResult;
QString m_hexResult;
bool m_isBinaryMode = false; // Changes the parser based on this variable
};
......@@ -14,6 +14,7 @@
void MathEngine::parse(QString expr) {
m_driver.parse(expr.toStdString());
m_result = m_driver.result;
m_error = m_driver.syntaxError;
emit resultChanged();
}
......@@ -35,7 +36,7 @@ QStringList MathEngine::getRegexMatches(const QString &expr,
void MathEngine::parseBinaryExpression(QString expr) {
m_error = true;
qDebug() << "Current Epxression (mathengine.cpp): " << expr;
// qDebug() << "Current Epxression (mathengine.cpp): " << expr;
int numbersPresent = 0;
int operatorsPresent = 0;
......@@ -51,7 +52,7 @@ void MathEngine::parseBinaryExpression(QString expr) {
} else {
m_error = false;
if (operatorsPresent == 0 && numbersPresent == 1) {
m_result = KNumber::binaryFromString(numbers[0]).toBinaryString(0);
m_result = KNumber::binaryFromString(numbers[0]);
emit resultChanged();
}
}
......@@ -99,7 +100,7 @@ void MathEngine::parseBinaryExpression(QString expr) {
default: // error
m_error = true;
};
m_result = result.toBinaryString(0);
m_result = result;
emit resultChanged();
}
}
......@@ -11,9 +11,11 @@
#include <QObject>
#include <QRegularExpression>
#include <QStringList>
#include <knumber.h>
class MathEngine : public QObject {
Q_OBJECT
Q_PROPERTY(QString result READ result NOTIFY resultChanged)
Q_PROPERTY(KNumber result READ result NOTIFY resultChanged)
Q_PROPERTY(bool error READ error NOTIFY resultChanged)
public:
static MathEngine *inst() {
......@@ -22,15 +24,15 @@ public:
}
Q_INVOKABLE void parse(QString expr);
Q_INVOKABLE void parseBinaryExpression(QString expr);
QString result() { return m_result; };
bool error() { return m_driver.syntaxError || m_error; };
KNumber result() { return m_result; };
bool error() { return m_error; };
signals:
void resultChanged();
private:
MathEngine(){};
driver m_driver;
QString m_result;
KNumber m_result;
bool m_error;
const QString bitRegex = QString("[01]+");
const QString binaryOperatorRegex =
......
......@@ -20,7 +20,7 @@ public:
driver();
std::map<std::string, int> variables;
QString result;
KNumber result;
int parse(const std::string expr);
// Whether to generate parser debug traces.
......
......@@ -80,7 +80,7 @@
%%
%start unit;
unit: exp { drv.result = $1.toQString(); };
unit: exp { drv.result = $1; };
%left "+" "-";
%left "*" "/";
......
......@@ -17,9 +17,6 @@ Kirigami.Page {
rightPadding: 0
bottomPadding: 0
property int yTranslate: 0
property real mainOpacity: 1
property color dropShadowColor: Qt.darker(Kirigami.Theme.backgroundColor, 1.15)
property int keypadHeight: {
let rows = 6, columns = 5;
......@@ -63,8 +60,7 @@ Kirigami.Page {
}
onIsCurrentPageChanged: {
if (!inputManager.binaryMode())
inputManager.setBinaryMode(true);
inputManager.setBinaryMode(true)
}
background: Rectangle {
......@@ -74,125 +70,154 @@ Kirigami.Page {
anchors.fill: parent
}
Item {
// top panel drop shadow
RectangularGlow {
anchors.fill: topPanelBackground
anchors.topMargin: 1
glowRadius: 4
spread: 0.2
color: dropShadowColor
}
Rectangle {
id: topPanelBackground
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Kirigami.Theme.backgroundColor
implicitHeight: outputScreen.height
}
ColumnLayout {
anchors.fill: parent
opacity: mainOpacity
transform: Translate { y: yTranslate }
spacing: 0
// top panel drop shadow
RectangularGlow {
anchors.fill: topPanelBackground
anchors.topMargin: 1
glowRadius: 4
spread: 0.2
color: dropShadowColor
}
Rectangle {
id: topPanelBackground
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Kirigami.Theme.backgroundColor
implicitHeight: outputScreen.height
}
ColumnLayout {
id: mainScreen
anchors.fill: parent
spacing: 0
Item {
id: outputScreen
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: initialPage.height - initialPage.keypadHeight
Item {
id: outputScreen
Layout.fillWidth: true
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: initialPage.height - initialPage.keypadHeight
Column {
id: outputColumn
anchors.fill: parent
anchors.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.gridUnit
Column {
id: outputColumn
anchors.fill: parent
anchors.margins: Kirigami.Units.largeSpacing
spacing: Kirigami.Units.gridUnit
Flickable {
anchors.right: parent.right
height: Kirigami.Units.gridUnit * 1.5
width: Math.min(parent.width, contentWidth)
contentHeight: expressionRow.height
contentWidth: expressionRow.width
flickableDirection: Flickable.HorizontalFlick
Controls.Label {
id: expressionRow
horizontalAlignment: Text.AlignRight
font.pointSize: Kirigami.Units.gridUnit
font.weight: Font.Light
text: inputManager.expression
color: Kirigami.Theme.disabledTextColor
}
onContentWidthChanged: {
if(contentWidth > width)
contentX = contentWidth - width;
}
Flickable {
anchors.right: parent.right
height: Kirigami.Units.gridUnit * 1.5
width: Math.min(parent.width, contentWidth)
contentHeight: expressionRow.height
contentWidth: expressionRow.width
flickableDirection: Flickable.HorizontalFlick
Controls.Label {
id: expressionRow
horizontalAlignment: Text.AlignRight
font.pointSize: Kirigami.Units.gridUnit
font.weight: Font.Light
text: inputManager.expression
color: Kirigami.Theme.disabledTextColor
}
Flickable {
anchors.right: parent.right
height: Kirigami.Units.gridUnit * 4
width: Math.min(parent.width, contentWidth)
contentHeight: result.height
contentWidth: result.width
flickableDirection: Flickable.HorizontalFlick
Controls.Label {
id: result
horizontalAlignment: Text.AlignRight
font.pointSize: Kirigami.Units.gridUnit * 2
font.weight: Font.Light
text: inputManager.result
NumberAnimation on opacity {
id: resultFadeInAnimation
from: 0.5
to: 1
duration: Kirigami.Units.shortDuration
}
NumberAnimation on opacity {
id: resultFadeOutAnimation
from: 1
to: 0
duration: Kirigami.Units.shortDuration
}
onTextChanged: resultFadeInAnimation.start()
}
onContentWidthChanged: {
if(contentWidth > width)
contentX = contentWidth - width;
}
}
Flickable {
anchors.right: parent.right
height: Kirigami.Units.gridUnit * 2
width: Math.min(parent.width, contentWidth)
contentHeight: resultBin.height
contentWidth: resultBin.width
flickableDirection: Flickable.HorizontalFlick
Controls.Label {
id: resultBin
horizontalAlignment: Text.AlignRight
font.pointSize: Kirigami.Units.gridUnit * 1.5
font.weight: Font.Light
text: inputManager.binaryResult
onTextChanged: resultFadeInAnimation.start()
}
}
Flickable {
anchors.right: parent.right
height: Kirigami.Units.gridUnit * 2
width: Math.min(parent.width, contentWidth)
contentHeight: resultDec.height
contentWidth: resultDec.width
flickableDirection: Flickable.HorizontalFlick
Controls.Label {
id: resultDec
horizontalAlignment: Text.AlignRight
font.pointSize: Kirigami.Units.gridUnit * 1.5
font.weight: Font.Light
text: inputManager.result
onTextChanged: resultFadeInAnimation.start()
}
}
Flickable {
anchors.right: parent.right
height: Kirigami.Units.gridUnit * 2
width: Math.min(parent.width, contentWidth)
contentHeight: resultHex.height
contentWidth: resultHex.width
flickableDirection: Flickable.HorizontalFlick
Controls.Label {
id: resultHex
horizontalAlignment: Text.AlignRight
font.pointSize: Kirigami.Units.gridUnit * 1.5
font.weight: Font.Light
text: inputManager.hexResult
onTextChanged: resultFadeInAnimation.start()
}
}
NumberAnimation on opacity {
id: resultFadeInAnimation
from: 0.5
to: 1
duration: Kirigami.Units.shortDuration
exclude: [expressionRow]
}
NumberAnimation on opacity {
id: resultFadeOutAnimation
from: 1
to: 0
duration: Kirigami.Units.shortDuration
exclude: [expressionRow]
}
}
}
// Binary Input Pad
Item {
property string expression: ""
id: binaryInputPad
Layout.fillHeight: true
Layout.preferredWidth: initialPage.width
Layout.alignment: Qt.AlignLeft
// Binary Input Pad
Item {
property string expression: ""
id: binaryInputPad
Layout.fillHeight: true
Layout.preferredWidth: initialPage.width
Layout.alignment: Qt.AlignLeft
BinaryPad {
id: binaryPad
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
// Uncomment next line for function overlay
// anchors.rightMargin: Kirigami.Units.gridUnit * 1.5
onPressed: {
if (text == "DEL") {
inputManager.backspace();
} else if (text == "=") {
inputManager.equal();
resultFadeOutAnimation.start();
} else {
inputManager.append(text, true);
}
BinaryPad {
id: binaryPad
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
// Uncomment next line for function overlay
// anchors.rightMargin: Kirigami.Units.gridUnit * 1.5
onPressed: {
if (text == "DEL") {
inputManager.backspace();
} else if (text == "=") {
inputManager.equal();
resultFadeOutAnimation.start();
} else {
inputManager.append(text, true);
}
onClear: inputManager.clear()
}
onClear: inputManager.clear()
}
}
}
......
......@@ -36,6 +36,6 @@ GridLayout {
// Row number from bottom: 1
NumberButton {text: "0"; onClicked: pressed(text);}
NumberButton {text: "1"; onClicked: pressed(text);}
NumberButton {text: "."; onClicked: pressed(text);}
NumberButton {text: "";}
NumberButton {text: "="; onClicked: pressed(text); special: true;}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment