added implementation in GO lang
This commit is contained in:
235
internal/modes/calculator.go
Normal file
235
internal/modes/calculator.go
Normal file
@ -0,0 +1,235 @@
|
||||
package modes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
|
||||
"kidskeyboard/internal/ui"
|
||||
)
|
||||
|
||||
type CalculatorMode struct {
|
||||
ctx Context
|
||||
display string
|
||||
acc float64
|
||||
op string
|
||||
newEntry bool
|
||||
err bool
|
||||
}
|
||||
|
||||
func NewCalculatorMode(ctx Context) *CalculatorMode {
|
||||
return &CalculatorMode{ctx: ctx, display: "0", newEntry: true}
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) Name() string { return "CTRL+F4 Calculator" }
|
||||
func (m *CalculatorMode) OnEnter() {}
|
||||
func (m *CalculatorMode) OnLeave() {}
|
||||
func (m *CalculatorMode) Update() {}
|
||||
|
||||
func (m *CalculatorMode) HandleInput() {
|
||||
handled := false
|
||||
valid := false
|
||||
chars := ebiten.AppendInputChars(nil)
|
||||
|
||||
for _, r := range chars {
|
||||
handled = true
|
||||
if m.handleChar(r) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range justPressedKeys() {
|
||||
switch {
|
||||
case key == ebiten.KeyDelete:
|
||||
handled = true
|
||||
m.clear()
|
||||
valid = true
|
||||
case key == ebiten.KeyBackspace:
|
||||
handled = true
|
||||
m.backspace()
|
||||
valid = true
|
||||
case key == ebiten.KeyEnter || key == ebiten.KeyNumpadEnter:
|
||||
handled = true
|
||||
m.equals()
|
||||
valid = true
|
||||
case isNumpadOperatorKey(key) && len(chars) == 0:
|
||||
handled = true
|
||||
m.operator(operatorForKey(key))
|
||||
valid = true
|
||||
default:
|
||||
if childKey(key) && len(chars) == 0 {
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if valid {
|
||||
m.ctx.Audio.PlayBeep()
|
||||
} else if handled {
|
||||
m.ctx.Audio.PlayError()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) handleChar(r rune) bool {
|
||||
switch {
|
||||
case r >= '0' && r <= '9':
|
||||
m.digit(string(r))
|
||||
return true
|
||||
case r == '+' || r == '-' || r == '*' || r == '/':
|
||||
m.operator(string(r))
|
||||
return true
|
||||
case r == 'c' || r == 'C':
|
||||
m.clear()
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) Draw(screen *ebiten.Image) {
|
||||
screen.Fill(color.Black)
|
||||
w, h := screen.Bounds().Dx(), screen.Bounds().Dy()
|
||||
ui.Text(screen, "CTRL+F4", 20, 20, color.White, 2)
|
||||
|
||||
panelW := float32(min(float64(w)*0.78, 620))
|
||||
keySize := panelW / 4
|
||||
startX := (float32(w) - panelW) / 2
|
||||
startY := float32(h)/2 - keySize*1.9
|
||||
displayH := keySize * 0.72
|
||||
|
||||
ui.Rect(screen, startX, startY-displayH-16, panelW, displayH, color.NRGBA{R: 20, G: 20, B: 20, A: 255})
|
||||
ui.RectOutline(screen, startX, startY-displayH-16, panelW, displayH, 2, color.White)
|
||||
scale := 4.0
|
||||
if len(m.display) > 12 {
|
||||
scale = 3
|
||||
}
|
||||
ui.CenteredText(screen, m.display, int(startX+panelW/2), int(startY-displayH/2-16), color.White, scale)
|
||||
|
||||
keys := [][]string{
|
||||
{"7", "8", "9", "/"},
|
||||
{"4", "5", "6", "*"},
|
||||
{"1", "2", "3", "-"},
|
||||
{"C", "0", "=", "+"},
|
||||
}
|
||||
for r, row := range keys {
|
||||
for c, label := range row {
|
||||
x := startX + float32(c)*keySize
|
||||
y := startY + float32(r)*keySize
|
||||
ui.Rect(screen, x+4, y+4, keySize-8, keySize-8, color.NRGBA{R: 28, G: 28, B: 28, A: 255})
|
||||
ui.RectOutline(screen, x+4, y+4, keySize-8, keySize-8, 2, color.White)
|
||||
ui.CenteredText(screen, label, int(x+keySize/2), int(y+keySize/2), color.White, 4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) digit(d string) {
|
||||
if m.err || m.newEntry || m.display == "0" {
|
||||
m.display = d
|
||||
m.newEntry = false
|
||||
m.err = false
|
||||
return
|
||||
}
|
||||
if len(m.display) < 16 {
|
||||
m.display += d
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) operator(op string) {
|
||||
if m.err {
|
||||
return
|
||||
}
|
||||
if m.op != "" && !m.newEntry {
|
||||
m.equals()
|
||||
}
|
||||
m.acc = m.value()
|
||||
m.op = op
|
||||
m.newEntry = true
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) equals() {
|
||||
if m.err || m.op == "" {
|
||||
m.newEntry = true
|
||||
return
|
||||
}
|
||||
right := m.value()
|
||||
var result float64
|
||||
switch m.op {
|
||||
case "+":
|
||||
result = m.acc + right
|
||||
case "-":
|
||||
result = m.acc - right
|
||||
case "*":
|
||||
result = m.acc * right
|
||||
case "/":
|
||||
if right == 0 {
|
||||
m.display = "DIV 0"
|
||||
m.err = true
|
||||
m.op = ""
|
||||
m.newEntry = true
|
||||
return
|
||||
}
|
||||
result = m.acc / right
|
||||
}
|
||||
m.display = formatNumber(result)
|
||||
m.acc = result
|
||||
m.op = ""
|
||||
m.newEntry = true
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) backspace() {
|
||||
if m.err || m.newEntry || len(m.display) <= 1 {
|
||||
m.display = "0"
|
||||
m.err = false
|
||||
m.newEntry = true
|
||||
return
|
||||
}
|
||||
m.display = m.display[:len(m.display)-1]
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) clear() {
|
||||
m.display = "0"
|
||||
m.acc = 0
|
||||
m.op = ""
|
||||
m.err = false
|
||||
m.newEntry = true
|
||||
}
|
||||
|
||||
func (m *CalculatorMode) value() float64 {
|
||||
v, _ := strconv.ParseFloat(m.display, 64)
|
||||
return v
|
||||
}
|
||||
|
||||
func formatNumber(v float64) string {
|
||||
if math.Abs(v-math.Round(v)) < 0.0000001 {
|
||||
return fmt.Sprintf("%.0f", v)
|
||||
}
|
||||
return strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.6f", v), "0"), ".")
|
||||
}
|
||||
|
||||
func isNumpadOperatorKey(key ebiten.Key) bool {
|
||||
switch key {
|
||||
case ebiten.KeyNumpadAdd, ebiten.KeyNumpadSubtract, ebiten.KeyNumpadMultiply, ebiten.KeyNumpadDivide:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func operatorForKey(key ebiten.Key) string {
|
||||
switch key {
|
||||
case ebiten.KeyNumpadAdd:
|
||||
return "+"
|
||||
case ebiten.KeyNumpadSubtract:
|
||||
return "-"
|
||||
case ebiten.KeyNumpadMultiply:
|
||||
return "*"
|
||||
case ebiten.KeyNumpadDivide:
|
||||
return "/"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user