Files
MujicaUI/code/debug_toolbar.go
T

145 lines
4.6 KiB
Go

package code
import (
"fmt"
"log/slog"
"runtime"
"github.com/egoist/mygo/ui"
"github.com/ZacharyZhang-NY/MujicaUI/core"
"github.com/ZacharyZhang-NY/MujicaUI/navigation"
)
// DebugState is where a debug session is.
type DebugState int
const (
DebugStopped DebugState = iota
DebugRunning
DebugPaused
)
// DebugAction is what the user asked the debugger to do.
type DebugAction int
const (
DebugNone DebugAction = iota
// DebugContinue resumes a paused session or starts a stopped one.
DebugContinue
DebugPause
DebugStepOver
DebugStepInto
DebugStepOut
DebugRestart
DebugStop
)
// DebugToolbarOptions configure a DebugToolbar.
type DebugToolbarOptions struct {
Disabled bool
}
// DebugToolbarResult carries the requested action.
type DebugToolbarResult struct {
Element *ui.Element
Action DebugAction
}
// debugIcon draws a toolbar glyph in a 24-unit box.
func debugIcon(body string) *ui.SVG {
return ui.MustParseSVG([]byte(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">` + body + `</svg>`))
}
var debugIcons = map[DebugAction]*ui.SVG{
DebugContinue: debugIcon(`<path d="M7 4l12 8-12 8z" fill="currentColor"/>`),
DebugPause: debugIcon(`<path d="M8 5v14M16 5v14"/>`),
DebugStepOver: debugIcon(`<path d="M4 13a8 8 0 0 1 15-3"/><path d="M20 5v5h-5"/><circle cx="12" cy="19" r="2" fill="currentColor"/>`),
DebugStepInto: debugIcon(`<path d="M12 3v11"/><path d="M7 9l5 5 5-5"/><circle cx="12" cy="20" r="2" fill="currentColor"/>`),
DebugStepOut: debugIcon(`<path d="M12 15V4"/><path d="M7 9l5-5 5 5"/><circle cx="12" cy="20" r="2" fill="currentColor"/>`),
DebugRestart: debugIcon(`<path d="M4 12a8 8 0 1 0 3-6.2"/><path d="M4 4v5h5"/>`),
DebugStop: debugIcon(`<rect x="6" y="6" width="12" height="12" rx="1" fill="currentColor"/>`),
}
// debugShortcut is an action's key, as in common IDEs.
type debugShortcut struct {
mods ui.Modifiers
key ui.Key
name string
}
func debugShortcuts() map[DebugAction]debugShortcut {
cmd := "Ctrl"
if runtime.GOOS == "darwin" {
cmd = "⌘"
}
return map[DebugAction]debugShortcut{
DebugContinue: {0, ui.KeyF5, "F5"},
DebugPause: {0, ui.KeyF6, "F6"},
DebugStepOver: {0, ui.KeyF10, "F10"},
DebugStepInto: {0, ui.KeyF11, "F11"},
DebugStepOut: {ui.Shift, ui.KeyF11, "Shift+F11"},
DebugRestart: {ui.Cmd | ui.Shift, ui.KeyF5, cmd + "+Shift+F5"},
DebugStop: {ui.Shift, ui.KeyF5, "Shift+F5"},
}
}
// DebugEnabled reports whether action applies in state.
func DebugEnabled(state DebugState, a DebugAction) bool {
switch a {
case DebugContinue:
return state != DebugRunning
case DebugPause:
return state == DebugRunning
case DebugStepOver, DebugStepInto, DebugStepOut:
return state == DebugPaused
case DebugRestart, DebugStop:
return state != DebugStopped
}
return false
}
// DebugToolbar shows debugger actions enabled by state.
func DebugToolbar(c *ui.Context, state DebugState, opts DebugToolbarOptions) DebugToolbarResult {
if state < DebugStopped || state > DebugPaused {
panic("mujica: DebugState out of range")
}
keys := debugShortcuts()
var res DebugToolbarResult
res.Element = navigation.Toolbar(c, navigation.ToolbarOptions{Label: core.Msg(c, "debug_toolbar.label")}, func() {
first := DebugContinue
if state == DebugRunning {
first = DebugPause
}
for _, a := range []DebugAction{first, DebugStepOver, DebugStepInto, DebugStepOut, DebugRestart, DebugStop} {
name := core.Msg(c, debugKeys[a])
if a == DebugContinue && state == DebugStopped {
name = core.Msg(c, "debug_toolbar.start")
}
on := DebugEnabled(state, a) && !opts.Disabled
b := navigation.ToolbarButton(c, name, navigation.ToolbarButtonOptions{Icon: debugIcons[a], Disabled: !on})
b.Tooltip(fmt.Sprintf("%s (%s)", name, keys[a].name))
if b.Clicked() && on && !b.IsDisabled() {
res.Action = a
}
}
}).Disabled(opts.Disabled)
if res.Action == DebugNone && !res.Element.IsDisabled() {
for _, a := range []DebugAction{DebugContinue, DebugPause, DebugStepOver, DebugStepInto, DebugStepOut, DebugRestart, DebugStop} {
if k := keys[a]; DebugEnabled(state, a) && c.Shortcut(k.mods, k.key) {
res.Action = a
break
}
}
}
if res.Action != DebugNone {
slog.Debug("mujica: debug_toolbar action", "action", res.Action, "state", state)
}
return res
}
var debugKeys = map[DebugAction]string{
DebugContinue: "debug_toolbar.continue", DebugPause: "debug_toolbar.pause", DebugStepOver: "debug_toolbar.stepOver",
DebugStepInto: "debug_toolbar.stepInto", DebugStepOut: "debug_toolbar.stepOut", DebugRestart: "debug_toolbar.restart", DebugStop: "debug_toolbar.stop",
}