diff --git a/Calculator.xcodeproj/project.pbxproj b/Calculator.xcodeproj/project.pbxproj index b11a288..b4616d7 100644 --- a/Calculator.xcodeproj/project.pbxproj +++ b/Calculator.xcodeproj/project.pbxproj @@ -319,7 +319,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.3.1; + MARKETING_VERSION = 0.5.0; PRODUCT_BUNDLE_IDENTIFIER = anthonyingle.Calculator; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; @@ -347,7 +347,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.3.1; + MARKETING_VERSION = 0.5.0; PRODUCT_BUNDLE_IDENTIFIER = anthonyingle.Calculator; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; diff --git a/Calculator/ButtonView.swift b/Calculator/ButtonView.swift index e062f15..9bfd28f 100644 --- a/Calculator/ButtonView.swift +++ b/Calculator/ButtonView.swift @@ -10,62 +10,111 @@ import SwiftUI struct ButtonView: View { @Binding var expression: String @Binding var history: [History] + @Binding var historyIndex: Int var body: some View { HStack { Button("C") { expression = "" + historyIndex = -1 }.calcButton() Button { + expression == "" ? addAnswer() : nil expression += "+" + historyIndex = -1 } label: { Image(systemName: "plus") }.calcButton() Button() { + expression == "" ? addAnswer() : nil expression += "-" + historyIndex = -1 } label: { Image(systemName: "minus") }.calcButton() Button() { + expression == "" ? addAnswer() : nil expression += "*" + historyIndex = -1 } label: { Image(systemName: "multiply") }.calcButton() Button { + expression == "" ? addAnswer() : nil expression += "/" + historyIndex = -1 } label: { Image(systemName: "divide") }.calcButton() - Button ("Ans") { - if (!history.isEmpty) + Button { + // check that history array is not empty and history index does not go out of bounds + if (!history.isEmpty && historyIndex < history.count - 1) { - expression += history[0].solution + historyIndex += 1 + + // if incrementing history, remove the number of characters of previous addition + if historyIndex > 0 { + expression.removeLast(history[historyIndex - 1].solution.count) + } + expression += history[historyIndex].solution } - }.calcButton().frame(width: 40) + } label: { + Image(systemName: "arrow.up") + }.calcButton() } HStack { + Button("-") { + expression += "-" + historyIndex = -1 + }.calcButton() + Button { expression += "sqrt(" + historyIndex = -1 } label: { Image(systemName: "x.squareroot") }.calcButton() + Button ("^") { + expression == "" ? addAnswer() : nil expression += "^" + historyIndex = -1 }.calcButton() - Button ("%") { - expression += "%" - }.calcButton() + Button ("(") { expression += "(" + historyIndex = -1 }.calcButton() + Button (")") { expression += ")" + historyIndex = -1 + }.calcButton() + + Button { + + if (!history.isEmpty) + { + // check if UP ARROW has been pressed yet + if historyIndex != -1 { + // if decrementing history, remove the number of characters of previous addition + expression.removeLast(history[historyIndex].solution.count) + historyIndex -= 1 + + // if we are not at the beginning of history, add the next solution in + if historyIndex != -1 { + expression += history[historyIndex].solution + } + } + } + } label: { + Image(systemName: "arrow.down") }.calcButton() } @@ -93,4 +142,11 @@ struct ButtonView: View { }.calcButton() } } + + func addAnswer() { + if (!history.isEmpty) + { + expression += history[0].solution + } + } } diff --git a/Calculator/CalculatorApp.swift b/Calculator/CalculatorApp.swift index dcf97c0..97994ab 100644 --- a/Calculator/CalculatorApp.swift +++ b/Calculator/CalculatorApp.swift @@ -21,4 +21,3 @@ struct CalculatorApp: App { } } } - diff --git a/Calculator/CalculatorView.swift b/Calculator/CalculatorView.swift index bff801b..6e2eef5 100644 --- a/Calculator/CalculatorView.swift +++ b/Calculator/CalculatorView.swift @@ -11,6 +11,9 @@ struct CalculatorView: View { @Binding var expression: String @Binding var solution: Double @Binding var history: [History] + + @State private var historyIndex: Int = -1 + var body: some View { // this is an upside down scroll view to show history @@ -58,9 +61,10 @@ struct CalculatorView: View { CustomMacTextView(placeholderText: "Calculate", text: $expression, - // Run when submmitting the text (hitting return) + // on ENTER key press onSubmit: { do { + historyIndex = -1 solution = try evaluateExpression(expression) // insert item into history @@ -76,14 +80,47 @@ struct CalculatorView: View { solution = 0 } }, - // Run on every update of the text + // On every update of the text by keyboard onTextChange: { newExpression in do { + historyIndex = -1 // reset the history index counter for arrow keys solution = try evaluateExpression(newExpression) } catch { solution = 0 } + }, + // on UP ARROW key + onMoveUp: { + // check that history array is not empty and history index does not go out of bounds + if (!history.isEmpty && historyIndex < history.count - 1) + { + historyIndex += 1 + + // if incrementing history, remove the number of characters of previous addition + if historyIndex > 0 { + expression.removeLast(history[historyIndex - 1].solution.count) + } + expression += history[historyIndex].solution + } + + }, + // on DOWN ARROW key + onMoveDown: { + if (!history.isEmpty) + { + // check if UP ARROW has been pressed yet + if historyIndex != -1 { + // if decrementing history, remove the number of characters of previous addition + expression.removeLast(history[historyIndex].solution.count) + historyIndex -= 1 + + // if we are not at the beginning of history, add the next solution in + if historyIndex != -1 { + expression += history[historyIndex].solution + } + } + } }) .frame(height: 27) // MARK TODO: Make height-adjustable @@ -92,6 +129,6 @@ struct CalculatorView: View { .frame(maxWidth: .infinity, alignment: .trailing) .foregroundColor(.gray) - ButtonView(expression: $expression, history: $history) + ButtonView(expression: $expression, history: $history, historyIndex: $historyIndex) } } diff --git a/Calculator/CustomMacTextView.swift b/Calculator/CustomMacTextView.swift index 6056c48..d7507eb 100644 --- a/Calculator/CustomMacTextView.swift +++ b/Calculator/CustomMacTextView.swift @@ -19,6 +19,9 @@ struct CustomMacTextView: NSViewRepresentable { var onTextChange : (String) -> Void = { _ in } var onEditingChanged: () -> Void = {} + var onMoveUp : () -> Void = {} + var onMoveDown : () -> Void = {} + func makeCoordinator() -> Coordinator { Coordinator(self) } @@ -30,6 +33,7 @@ struct CustomMacTextView: NSViewRepresentable { textView.string = text textView.drawsBackground = false textView.font = font + textView.allowsUndo = true textView.placeholderText = placeholderText theTextView.hasVerticalScroller = false @@ -56,6 +60,7 @@ extension CustomMacTextView { init(_ parent: CustomMacTextView) { self.parent = parent } + func textDidBeginEditing(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return @@ -92,9 +97,19 @@ extension CustomMacTextView { // handles commands func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { if (commandSelector == #selector(NSResponder.insertNewline(_:))) { - // Do something against ENTER key + // Do something when ENTER key pressed self.parent.onSubmit() return true + + } else if (commandSelector == #selector(NSResponder.moveUp(_:))) { + // Do something when UP ARROW pressed + self.parent.onMoveUp() + return true + + } else if (commandSelector == #selector(NSResponder.moveDown(_:))) { + // Do something when DOWN ARROW pressed + self.parent.onMoveDown() + return true } // return true if the action was handled; otherwise false diff --git a/Calculator/HelperFunctions.swift b/Calculator/HelperFunctions.swift index 39ab49e..95c5407 100644 --- a/Calculator/HelperFunctions.swift +++ b/Calculator/HelperFunctions.swift @@ -15,6 +15,7 @@ struct History: Identifiable { } extension Double { + // removes zeros from the end of a Double when converting to String func removeZerosFromEnd() -> String { let formatter = NumberFormatter() let number = NSNumber(value: self) @@ -51,7 +52,7 @@ struct CalcButtonStyle: ButtonStyle { } } -extension View { +extension Button { func calcButton( foregroundColor: Color = .white, backgroundColor: Color = .gray,