package main import ( "fmt" "image/color" "os" "strconv" "strings" g "github.com/AllenDang/giu" ) const ( MasterWidth int = 378 Version string = "0.2.0" FullStack int = iota TopThree TopOfStack Dsc Asc ) type Input struct { buf strings.Builder decimal bool negative bool } func (i *Input) String() string { return i.buf.String() } func (i *Input) Append(value rune) { switch value { case '.': if i.decimal { return } if i.buf.Len() == 0 { i.buf.WriteRune('0') } i.decimal = true i.buf.WriteRune(value) case '-': if i.buf.Len() > 0 { return } i.negative = true i.buf.WriteRune(value) default: i.buf.WriteRune(value) } inputDisplay = i.String() SetComboOut() } func (i *Input) Store() { if i.buf.Len() == 0 { return } if i.buf.Len() == 1 && (i.negative || i.decimal) { i.Reset() return } num, err := strconv.ParseFloat(i.buf.String(), 64) if err != nil { panic("Bad input") } s.Push(num) i.Reset() inputDisplay = "" } func (i *Input) Reset() { i.buf.Reset() i.decimal = false i.negative = false } func (i *Input) Delete() { if i.buf.Len() == 0 { return } val := i.buf.String() popped := val[len(val)-1] i.buf.Reset() i.buf.WriteString(val[:len(val)-1]) if popped == '.' { i.decimal = false } else if popped == '-' { i.negative = false } inputDisplay = i.String() SetComboOut() } var ( wnd *g.MasterWindow inputBuffer *Input = &Input{} s *stack = &stack{-1, [100]float64{}} stackStyle int = FullStack inputDisplay string = inputBuffer.String() outputDisplay string = s.String() comboDisplay string = inputBuffer.String() Display *g.InputTextWidget = g.InputText("", &inputDisplay) Output *g.InputTextMultilineWidget = g.InputTextMultiline("", &outputDisplay) ComboView *g.InputTextWidget = g.InputText("", &comboDisplay) ShowStack bool = true stackSort int = Dsc stackLabel *g.LabelWidget = g.Label("Stack Output\n[d]{TOS ...}") ) func callStackWord(f func()) { inputBuffer.Store() f() outputDisplay = s.String() SetComboOut() } func SetComboOut() { if inputBuffer.buf.Len() > 0 { comboDisplay = inputBuffer.String() } else { comboDisplay = strconv.FormatFloat(s.Peek(), 'f', -1, 64) } } func SetStackLabelWithSort() { sort := "TOS ..." if stackSort == Asc { sort = "... TOS" } stackLabel = g.Label(fmt.Sprintf("Stack Output\n[d]{%s}", sort)) outputDisplay = s.String() } func ToggleScreenSize() { width := MasterWidth if ShowStack { width = MasterWidth + MasterWidth / 2 } wnd.SetSize(width,400) } func loop() { g.SingleWindowWithMenuBar("gnums - " + Version).Layout( g.MenuBar().Layout( g.Menu("gnums").Layout( g.Label("v"+Version), g.Checkbox("Show Stack Window", &ShowStack).OnChange(ToggleScreenSize), g.Menu("Output Style").Layout( g.RadioButton("Full Stack", stackStyle == FullStack).OnChange(func(){stackStyle = FullStack; outputDisplay = s.String()}), g.RadioButton("Top Three", stackStyle == TopThree).OnChange(func(){stackStyle = TopThree; outputDisplay = s.String()}), g.RadioButton("Top Of Stack", stackStyle == TopOfStack).OnChange(func(){stackStyle = TopOfStack; outputDisplay = s.String()}), ), g.Menu("Stack Sort").Layout( g.RadioButton("TOS ...", stackSort == Dsc).OnChange(func(){stackSort = Dsc; SetStackLabelWithSort()}), g.RadioButton("... TOS", stackSort == Asc).OnChange(func(){stackSort = Asc; SetStackLabelWithSort()}), ), g.MenuItem("Exit").OnClick(Exit), ), ), g.Dummy(-1, 8), ComboView.Size(-1).Flags(1 << 14), g.Dummy(-1, 8), g.Row( g.Condition(ShowStack, g.Layout{ g.Row( g.Child("Left").Layout( stackLabel, g.Tooltip("Stack display is\nnewest on top"), Output.Size(-1, -1).Flags(1 << 14), ).Size( float32(MasterWidth/2), -1, ), g.Child("Section").Layout( g.Row( g.Style().SetColor(g.StyleColorButton, color.RGBA{150, 55, 55, 255}).To( g.Button("Clear").Size(50,50).OnClick(func(){callStackWord(s.Clear)}), ), g.Button("Delete").Size(50,50).OnClick(func(){inputBuffer.Delete()}), g.Button("Round").Size(50,50).OnClick(func(){callStackWord(s.Round)}), g.Button("^").Size(50,50).OnClick(func(){callStackWord(s.Pow)}), g.Button("÷").Size(50,50).OnClick(func(){callStackWord(s.Div)}), g.Button("Sqrt").Size(50,50).OnClick(func(){callStackWord(s.Sqt)}), ), g.Row( g.Button("Over").Size(50,50).OnClick(func(){callStackWord(s.Ovr)}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Row( g.Button("7").Size(50,50).OnClick(func(){inputBuffer.Append('7')}), g.Button("8").Size(50,50).OnClick(func(){inputBuffer.Append('8')}), g.Button("9").Size(50,50).OnClick(func(){inputBuffer.Append('9')}), ), ), g.Button("×").Size(50,50).OnClick(func(){callStackWord(s.Mul)}), g.Button("Floor").Size(50,50).OnClick(func(){callStackWord(s.Floor)}), ), g.Row( g.Button("Swap").Size(50,50).OnClick(func(){callStackWord(s.Swp)}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Row( g.Button("4").Size(50,50).OnClick(func(){inputBuffer.Append('4')}), g.Button("5").Size(50,50).OnClick(func(){inputBuffer.Append('5')}), g.Button("6").Size(50,50).OnClick(func(){inputBuffer.Append('6')}), ), ), g.Button("-").Size(50,50).OnClick(func(){callStackWord(s.Sub)}), g.Button("Ceil").Size(50,50).OnClick(func(){callStackWord(s.Ceil)}), ), g.Row( g.Button("Dup").Size(50,50).OnClick(func(){callStackWord(s.Dup)}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Row( g.Button("1").Size(50,50).OnClick(func(){inputBuffer.Append('1')}), g.Button("2").Size(50,50).OnClick(func(){inputBuffer.Append('2')}), g.Button("3").Size(50,50).OnClick(func(){inputBuffer.Append('3')}), ), ), g.Button("+").Size(50,50).OnClick(func(){callStackWord(s.Add)}), g.Button("+/-").Size(50,50).OnClick(func(){callStackWord(s.Inv)}), ), g.Row( g.Button("Drop").Size(50,50).OnClick(func(){callStackWord(s.Drp)}), g.Button(".").Size(50,50).OnClick(func(){inputBuffer.Append('.')}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Button("0").Size(50,50).OnClick(func(){inputBuffer.Append('0')}), ), g.Style().SetColor(g.StyleColorButton, color.RGBA{51, 102, 55, 255}).To( g.Button("Enter").Size(165,50).OnClick(func(){inputBuffer.Store(); outputDisplay = s.String()}), ), ), ), ), }, g.Layout{ g.Child("Button Section").Layout( g.Row( g.Style().SetColor(g.StyleColorButton, color.RGBA{150, 55, 55, 255}).To( g.Button("Clear").Size(50,50).OnClick(func(){callStackWord(s.Clear)}), ), g.Button("Delete").Size(50,50).OnClick(func(){inputBuffer.Delete()}), g.Button("Round").Size(50,50).OnClick(func(){callStackWord(s.Round)}), g.Button("^").Size(50,50).OnClick(func(){callStackWord(s.Pow)}), g.Button("÷").Size(50,50).OnClick(func(){callStackWord(s.Div)}), g.Button("Sqrt").Size(50,50).OnClick(func(){callStackWord(s.Sqt)}), ), g.Row( g.Button("Over").Size(50,50).OnClick(func(){callStackWord(s.Ovr)}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Row( g.Button("7").Size(50,50).OnClick(func(){inputBuffer.Append('7')}), g.Button("8").Size(50,50).OnClick(func(){inputBuffer.Append('8')}), g.Button("9").Size(50,50).OnClick(func(){inputBuffer.Append('9')}), ), ), g.Button("×").Size(50,50).OnClick(func(){callStackWord(s.Mul)}), g.Button("Floor").Size(50,50).OnClick(func(){callStackWord(s.Floor)}), ), g.Row( g.Button("Swap").Size(50,50).OnClick(func(){callStackWord(s.Swp)}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Row( g.Button("4").Size(50,50).OnClick(func(){inputBuffer.Append('4')}), g.Button("5").Size(50,50).OnClick(func(){inputBuffer.Append('5')}), g.Button("6").Size(50,50).OnClick(func(){inputBuffer.Append('6')}), ), ), g.Button("-").Size(50,50).OnClick(func(){callStackWord(s.Sub)}), g.Button("Ceil").Size(50,50).OnClick(func(){callStackWord(s.Ceil)}), ), g.Row( g.Button("Dup").Size(50,50).OnClick(func(){callStackWord(s.Dup)}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Row( g.Button("1").Size(50,50).OnClick(func(){inputBuffer.Append('1')}), g.Button("2").Size(50,50).OnClick(func(){inputBuffer.Append('2')}), g.Button("3").Size(50,50).OnClick(func(){inputBuffer.Append('3')}), ), ), g.Button("+").Size(50,50).OnClick(func(){callStackWord(s.Add)}), g.Button("+/-").Size(50,50).OnClick(func(){callStackWord(s.Inv)}), ), g.Row( g.Button("Drop").Size(50,50).OnClick(func(){callStackWord(s.Drp)}), g.Button(".").Size(50,50).OnClick(func(){inputBuffer.Append('.')}), g.Style().SetColor(g.StyleColorButton, color.RGBA{205, 150, 30, 200}).To( g.Button("0").Size(50,50).OnClick(func(){inputBuffer.Append('0')}), ), g.Style().SetColor(g.StyleColorButton, color.RGBA{51, 102, 55, 255}).To( g.Button("Enter").Size(165,50).OnClick(func(){inputBuffer.Store(); outputDisplay = s.String()}), ), ), ), }), ), ) } func Exit() { os.Exit(0) } func main() { wnd = g.NewMasterWindow("gnums", MasterWidth+MasterWidth/2, 385, g.MasterWindowFlagsNotResizable).RegisterKeyboardShortcuts( g.WindowShortcut{Key: g.KeyQ, Modifier: g.ModControl, Callback: Exit}, g.WindowShortcut{Key: g.Key0, Callback: func(){inputBuffer.Append('0')}}, g.WindowShortcut{Key: g.Key1, Callback: func(){inputBuffer.Append('1')}}, g.WindowShortcut{Key: g.Key2, Callback: func(){inputBuffer.Append('2')}}, g.WindowShortcut{Key: g.Key3, Callback: func(){inputBuffer.Append('3')}}, g.WindowShortcut{Key: g.Key4, Callback: func(){inputBuffer.Append('4')}}, g.WindowShortcut{Key: g.Key5, Callback: func(){inputBuffer.Append('5')}}, g.WindowShortcut{Key: g.Key6, Callback: func(){inputBuffer.Append('6')}}, g.WindowShortcut{Key: g.Key7, Callback: func(){inputBuffer.Append('7')}}, g.WindowShortcut{Key: g.Key8, Callback: func(){inputBuffer.Append('8')}}, g.WindowShortcut{Key: g.Key9, Callback: func(){inputBuffer.Append('9')}}, g.WindowShortcut{Key: g.KeyPeriod, Callback: func(){inputBuffer.Append('.')}}, g.WindowShortcut{Key: g.KeyMinus, Callback: func(){callStackWord(s.Sub)}}, g.WindowShortcut{Key: g.KeyR, Callback: func(){callStackWord(s.Round)}}, g.WindowShortcut{Key: g.KeyC, Callback: func(){callStackWord(s.Ceil)}}, g.WindowShortcut{Key: g.KeyF, Callback: func(){callStackWord(s.Floor)}}, g.WindowShortcut{Key: g.KeyQ, Callback: func(){callStackWord(s.Sqt)}}, g.WindowShortcut{Key: g.KeyS, Callback: func(){callStackWord(s.Swp)}}, g.WindowShortcut{Key: g.KeyO, Callback: func(){callStackWord(s.Ovr)}}, g.WindowShortcut{Key: g.KeyD, Callback: func(){callStackWord(s.Drp)}}, g.WindowShortcut{Key: g.KeyU, Callback: func(){callStackWord(s.Dup)}}, g.WindowShortcut{Key: g.KeyEqual, Modifier: g.ModShift, Callback: func(){callStackWord(s.Add)}}, g.WindowShortcut{Key: g.Key8, Modifier: g.ModShift, Callback: func(){callStackWord(s.Mul)}}, g.WindowShortcut{Key: g.KeySlash, Callback: func(){callStackWord(s.Div)}}, g.WindowShortcut{Key: g.Key6, Modifier: g.ModShift, Callback: func(){callStackWord(s.Pow)}}, g.WindowShortcut{Key: g.KeyEnter, Callback: func(){inputBuffer.Store(); outputDisplay = s.String()}}, g.WindowShortcut{Key: g.KeyTab, Callback: func(){ShowStack = !ShowStack; ToggleScreenSize()}}, g.WindowShortcut{Key: g.KeyEscape, Callback: func(){callStackWord(s.Clear)}}, g.WindowShortcut{Key: g.Key1, Modifier: g.ModShift, Callback: func(){callStackWord(s.Inv)}}, g.WindowShortcut{Key: g.KeyBackspace, Callback: func(){inputBuffer.Delete()}}, ) wnd.Run(loop) }