erm/app/darktile/gui/gui.go

159 lines
3.8 KiB
Go
Raw Normal View History

2021-07-30 22:29:20 +00:00
package gui
import (
"fmt"
"image"
2024-02-07 23:43:18 +00:00
"log/slog"
2021-07-30 22:29:20 +00:00
"os"
"strings"
"time"
2023-01-16 02:18:08 +00:00
"github.com/hajimehoshi/ebiten/v2"
2024-02-07 23:43:18 +00:00
"go.uber.org/fx"
2023-01-16 06:32:17 +00:00
"tuxpa.in/t/erm/app/darktile/config"
2023-01-16 02:11:07 +00:00
"tuxpa.in/t/erm/app/darktile/font"
"tuxpa.in/t/erm/app/darktile/gui/popup"
"tuxpa.in/t/erm/app/darktile/hinters"
2024-02-07 23:43:18 +00:00
"tuxpa.in/t/erm/app/darktile/termutil"
2023-01-16 02:11:07 +00:00
termutil2 "tuxpa.in/t/erm/app/darktile/termutil"
2021-07-30 22:29:20 +00:00
)
type GUI struct {
mouseStateLeft MouseState
mouseStateRight MouseState
mouseStateMiddle MouseState
mouseDrag bool
size image.Point // pixels
2023-01-16 02:11:07 +00:00
terminal *termutil2.Terminal
2021-07-30 22:29:20 +00:00
updateChan chan struct{}
lastClick time.Time
clickCount int
fontManager *font.Manager
2023-01-16 02:11:07 +00:00
mousePos termutil2.Position
2021-07-30 22:29:20 +00:00
hinters []hinters.Hinter
activeHinter int
popupMessages []popup.Message
2021-07-30 22:29:20 +00:00
screenshotRequested bool
screenshotFilename string
startupFuncs []func(g *GUI)
keyState *keyState
opacity float64
enableLigatures bool
cursorImage *ebiten.Image
2024-02-07 23:43:18 +00:00
log *slog.Logger
theme *termutil.Theme
c *config.Lark
2021-07-30 22:29:20 +00:00
}
type MouseState uint8
const (
MouseStateNone MouseState = iota
MouseStatePressed
)
2024-02-07 23:43:18 +00:00
func New(terminal *termutil2.Terminal, c *config.Lark, log *slog.Logger) (*GUI, error) {
2021-07-30 22:29:20 +00:00
g := &GUI{
terminal: terminal,
size: image.Point{80, 30},
updateChan: make(chan struct{}),
fontManager: font.NewManager(),
activeHinter: -1,
keyState: newKeyState(),
enableLigatures: true,
2024-02-07 23:43:18 +00:00
c: c,
log: log,
theme: termutil.ThemeFromLark(c),
2021-07-30 22:29:20 +00:00
}
terminal.SetWindowManipulator(NewManipulator(g))
return g, nil
}
2024-02-07 23:43:18 +00:00
func (g *GUI) Run(s fx.Shutdowner) error {
2021-07-30 22:29:20 +00:00
go func() {
if err := g.terminal.Run(g.updateChan, uint16(g.size.X), uint16(g.size.Y)); err != nil {
fmt.Fprintf(os.Stderr, "Fatal error: %s", err)
2024-02-07 23:43:18 +00:00
s.Shutdown(fx.ExitCode(1))
2021-07-30 22:29:20 +00:00
}
2024-02-07 23:43:18 +00:00
s.Shutdown(fx.ExitCode(0))
2021-07-30 22:29:20 +00:00
}()
2024-02-07 23:43:18 +00:00
font, err := g.c.Font("font")
if err != nil {
return err
}
g.log.Info("running gui", "font", font)
g.fontManager.SetFontByFamilyName(font.Family)
if font.Size > 0 {
g.fontManager.SetSize(font.Size)
}
if len(font.Style) > 0 {
g.fontManager.SetFontStyle(font.Style)
}
g.fontManager.SetDPI(96)
g.enableLigatures = font.Ligatures
ebiten.SetWindowTitle("erm")
ebiten.SetScreenClearedEveryFrame(true)
2024-02-07 23:43:18 +00:00
ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled)
2021-07-30 22:29:20 +00:00
ebiten.SetRunnableOnUnfocused(true)
2024-02-07 23:43:18 +00:00
ebiten.SetTPS(144)
ebiten.SetScreenClearedEveryFrame(false)
if g.c.Truthy("vsync") {
ebiten.SetVsyncEnabled(true)
2023-01-16 06:32:17 +00:00
} else {
2024-02-07 23:43:18 +00:00
ebiten.SetVsyncEnabled(false)
2023-01-16 06:32:17 +00:00
}
2021-07-30 22:29:20 +00:00
for _, f := range g.startupFuncs {
go f(g)
}
return ebiten.RunGame(g)
}
func (g *GUI) CellSize() image.Point {
return g.fontManager.CharSize()
}
2023-01-16 02:11:07 +00:00
func (g *GUI) Highlight(start termutil2.Position, end termutil2.Position, label string, img image.Image) {
2021-07-30 22:29:20 +00:00
if label == "" && img == nil {
g.terminal.GetActiveBuffer().Highlight(start, end, nil)
return
}
2023-01-16 02:11:07 +00:00
annotation := &termutil2.Annotation{
2021-07-30 22:29:20 +00:00
Text: label,
Image: img,
}
if label != "" {
lines := strings.Split(label, "\n")
annotation.Height = float64(len(lines))
for _, line := range lines {
if float64(len(line)) > annotation.Width {
annotation.Width = float64(len(line))
}
}
}
if img != nil {
annotation.Height += float64(img.Bounds().Dy() / g.fontManager.CharSize().Y)
if label != "" {
annotation.Height += 0.5 // half line spacing between image + text
}
imgCellWidth := img.Bounds().Dx() / g.fontManager.CharSize().X
if float64(imgCellWidth) > annotation.Width {
annotation.Width = float64(imgCellWidth)
}
}
g.terminal.GetActiveBuffer().Highlight(start, end, annotation)
}
func (g *GUI) ClearHighlight() {
g.terminal.GetActiveBuffer().ClearHighlight()
}