mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-04-19 12:49:52 +03:00
Main Menu, main play state, pause menu
This commit is contained in:
parent
dce7d29a99
commit
c0f80f0e0c
24 changed files with 1053 additions and 86 deletions
41
game/game.go
Normal file
41
game/game.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/game/state"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
state state.GameState
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateGame() *Game {
|
||||||
|
game := new(Game)
|
||||||
|
|
||||||
|
game.state = state.NewMainMenuState()
|
||||||
|
|
||||||
|
return game
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Input(ev *tcell.EventKey) {
|
||||||
|
g.state.OnInput(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Tick(dt int64) bool {
|
||||||
|
s := g.state.OnTick(dt)
|
||||||
|
|
||||||
|
switch s.(type) {
|
||||||
|
case *state.QuitState:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
g.state = s
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(v views.View) {
|
||||||
|
g.state.OnDraw(v)
|
||||||
|
}
|
30
game/model/entity.go
Normal file
30
game/model/entity.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Up Direction = iota
|
||||||
|
Down
|
||||||
|
Left
|
||||||
|
Right
|
||||||
|
)
|
||||||
|
|
||||||
|
type Entity interface {
|
||||||
|
UniqueId() uuid.UUID
|
||||||
|
Draw(v views.View)
|
||||||
|
Input(e *tcell.EventKey)
|
||||||
|
Tick(dt int64)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MovableEntity interface {
|
||||||
|
Position() util.Position
|
||||||
|
Move(dir Direction)
|
||||||
|
}
|
76
game/model/player.go
Normal file
76
game/model/player.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
id uuid.UUID
|
||||||
|
position util.Position
|
||||||
|
style tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreatePlayer(x, y uint16, style tcell.Style) *Player {
|
||||||
|
p := new(Player)
|
||||||
|
|
||||||
|
p.id = uuid.New()
|
||||||
|
p.position = util.PositionAt(x, y)
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) UniqueId() uuid.UUID {
|
||||||
|
return p.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Move(dir Direction) {
|
||||||
|
x, y := p.position.XYUint16()
|
||||||
|
|
||||||
|
switch dir {
|
||||||
|
case Up:
|
||||||
|
p.position = util.PositionAt(x, y-1)
|
||||||
|
case Down:
|
||||||
|
p.position = util.PositionAt(x, y+1)
|
||||||
|
case Left:
|
||||||
|
p.position = util.PositionAt(x-1, y)
|
||||||
|
case Right:
|
||||||
|
p.position = util.PositionAt(x+1, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Draw(v views.View) {
|
||||||
|
x, y := p.position.XY()
|
||||||
|
v.SetContent(x, y, '@', nil, p.style)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Input(e *tcell.EventKey) {
|
||||||
|
switch e.Key() {
|
||||||
|
case tcell.KeyUp:
|
||||||
|
p.Move(Up)
|
||||||
|
case tcell.KeyDown:
|
||||||
|
p.Move(Down)
|
||||||
|
case tcell.KeyLeft:
|
||||||
|
p.Move(Left)
|
||||||
|
case tcell.KeyRight:
|
||||||
|
p.Move(Right)
|
||||||
|
case tcell.KeyRune:
|
||||||
|
switch e.Rune() {
|
||||||
|
case 'w':
|
||||||
|
p.Move(Up)
|
||||||
|
case 'a':
|
||||||
|
p.Move(Left)
|
||||||
|
case 's':
|
||||||
|
p.Move(Down)
|
||||||
|
case 'd':
|
||||||
|
p.Move(Right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Tick(dt int64) {
|
||||||
|
|
||||||
|
}
|
20
game/state/gameState.go
Normal file
20
game/state/gameState.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GameState interface {
|
||||||
|
OnInput(e *tcell.EventKey)
|
||||||
|
OnTick(dt int64) GameState
|
||||||
|
OnDraw(c views.View)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PausableState interface {
|
||||||
|
Pause()
|
||||||
|
Unpause()
|
||||||
|
SetPaused(paused bool)
|
||||||
|
|
||||||
|
GameState
|
||||||
|
}
|
87
game/state/mainMenuState.go
Normal file
87
game/state/mainMenuState.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/ui"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MainMenuState struct {
|
||||||
|
menuTitle *render.Raw
|
||||||
|
buttons []*ui.UISimpleButton
|
||||||
|
currButtonSelected int
|
||||||
|
|
||||||
|
quitGame bool
|
||||||
|
startNewGame bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMainMenuState() *MainMenuState {
|
||||||
|
state := new(MainMenuState)
|
||||||
|
|
||||||
|
highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold)
|
||||||
|
|
||||||
|
state.menuTitle = render.CreateRawDrawable(
|
||||||
|
11, 1, tcell.StyleDefault.Attributes(tcell.AttrBold).Foreground(tcell.ColorYellow),
|
||||||
|
" | | | _) | | ",
|
||||||
|
" | _` | __| __| | | _` | __ \\ __|",
|
||||||
|
" | ( | \\__ \\ | | | ( | | | | | ",
|
||||||
|
"_____| \\__,_| ____/ \\__| _____| _| \\__, | _| |_| \\__|",
|
||||||
|
" |___/ ",
|
||||||
|
)
|
||||||
|
state.buttons = make([]*ui.UISimpleButton, 0)
|
||||||
|
state.buttons = append(state.buttons, ui.CreateSimpleButton(11, 7, "New Game", tcell.StyleDefault, highlightStyle, func() {
|
||||||
|
state.startNewGame = true
|
||||||
|
}))
|
||||||
|
state.buttons = append(state.buttons, ui.CreateSimpleButton(11, 9, "Load Game", tcell.StyleDefault, highlightStyle, func() {
|
||||||
|
|
||||||
|
}))
|
||||||
|
state.buttons = append(state.buttons, ui.CreateSimpleButton(11, 11, "Quit", tcell.StyleDefault, highlightStyle, func() {
|
||||||
|
state.quitGame = true
|
||||||
|
}))
|
||||||
|
|
||||||
|
state.currButtonSelected = 0
|
||||||
|
state.buttons[state.currButtonSelected].Highlight()
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mms *MainMenuState) OnInput(e *tcell.EventKey) {
|
||||||
|
if e.Key() == tcell.KeyDown {
|
||||||
|
mms.buttons[mms.currButtonSelected].Unhighlight()
|
||||||
|
mms.currButtonSelected = util.LimitIncrement(mms.currButtonSelected, 2)
|
||||||
|
mms.buttons[mms.currButtonSelected].Highlight()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyUp {
|
||||||
|
mms.buttons[mms.currButtonSelected].Unhighlight()
|
||||||
|
mms.currButtonSelected = util.LimitDecrement(mms.currButtonSelected, 0)
|
||||||
|
mms.buttons[mms.currButtonSelected].Highlight()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyEnter {
|
||||||
|
mms.buttons[mms.currButtonSelected].Select()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mms *MainMenuState) OnTick(dt int64) GameState {
|
||||||
|
if mms.quitGame {
|
||||||
|
return &QuitState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mms.startNewGame {
|
||||||
|
return BeginPlayingState()
|
||||||
|
}
|
||||||
|
|
||||||
|
return mms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mms *MainMenuState) OnDraw(c views.View) {
|
||||||
|
mms.menuTitle.Draw(c)
|
||||||
|
|
||||||
|
for _, b := range mms.buttons {
|
||||||
|
b.Draw(c)
|
||||||
|
}
|
||||||
|
}
|
108
game/state/pauseGameState.go
Normal file
108
game/state/pauseGameState.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/ui"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PauseGameState struct {
|
||||||
|
prevState PausableState
|
||||||
|
|
||||||
|
unpauseGame bool
|
||||||
|
returnToMainMenu bool
|
||||||
|
|
||||||
|
pauseMenuWindow *ui.UIWindow
|
||||||
|
buttons []*ui.UISimpleButton
|
||||||
|
currButtonSelected int
|
||||||
|
}
|
||||||
|
|
||||||
|
func PauseGame(prevState PausableState) *PauseGameState {
|
||||||
|
s := new(PauseGameState)
|
||||||
|
|
||||||
|
s.prevState = prevState
|
||||||
|
|
||||||
|
highlightStyle := tcell.StyleDefault.Attributes(tcell.AttrBold)
|
||||||
|
|
||||||
|
s.pauseMenuWindow = ui.CreateWindow(uint16(render.TERMINAL_SIZE_WIDTH)/2-15, uint16(render.TERMINAL_SIZE_HEIGHT)/2-7, 30, 14, "PAUSED", tcell.StyleDefault)
|
||||||
|
s.buttons = make([]*ui.UISimpleButton, 0)
|
||||||
|
s.buttons = append(
|
||||||
|
s.buttons,
|
||||||
|
ui.CreateSimpleButton(
|
||||||
|
uint16(s.pauseMenuWindow.Position().X())+3,
|
||||||
|
uint16(s.pauseMenuWindow.Position().Y())+1,
|
||||||
|
"Resume",
|
||||||
|
tcell.StyleDefault,
|
||||||
|
highlightStyle,
|
||||||
|
func() {
|
||||||
|
s.unpauseGame = true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
s.buttons = append(
|
||||||
|
s.buttons,
|
||||||
|
ui.CreateSimpleButton(
|
||||||
|
uint16(s.pauseMenuWindow.Position().X())+3,
|
||||||
|
uint16(s.pauseMenuWindow.Position().Y())+3,
|
||||||
|
"Exit To Main Menu",
|
||||||
|
tcell.StyleDefault,
|
||||||
|
highlightStyle,
|
||||||
|
func() {
|
||||||
|
s.returnToMainMenu = true
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
s.currButtonSelected = 0
|
||||||
|
s.buttons[s.currButtonSelected].Highlight()
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pg *PauseGameState) OnInput(e *tcell.EventKey) {
|
||||||
|
if e.Key() == tcell.KeyEsc {
|
||||||
|
pg.unpauseGame = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyDown {
|
||||||
|
pg.buttons[pg.currButtonSelected].Unhighlight()
|
||||||
|
pg.currButtonSelected = util.LimitIncrement(pg.currButtonSelected, 1)
|
||||||
|
pg.buttons[pg.currButtonSelected].Highlight()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyUp {
|
||||||
|
pg.buttons[pg.currButtonSelected].Unhighlight()
|
||||||
|
pg.currButtonSelected = util.LimitDecrement(pg.currButtonSelected, 0)
|
||||||
|
pg.buttons[pg.currButtonSelected].Highlight()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyEnter {
|
||||||
|
pg.buttons[pg.currButtonSelected].Select()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pg *PauseGameState) OnTick(dt int64) GameState {
|
||||||
|
if pg.unpauseGame {
|
||||||
|
pg.prevState.Unpause()
|
||||||
|
return pg.prevState
|
||||||
|
}
|
||||||
|
|
||||||
|
if pg.returnToMainMenu {
|
||||||
|
return NewMainMenuState()
|
||||||
|
}
|
||||||
|
|
||||||
|
return pg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pg *PauseGameState) OnDraw(c views.View) {
|
||||||
|
pg.prevState.OnDraw(c)
|
||||||
|
|
||||||
|
pg.pauseMenuWindow.Draw(c)
|
||||||
|
|
||||||
|
for _, b := range pg.buttons {
|
||||||
|
b.Draw(c)
|
||||||
|
}
|
||||||
|
}
|
56
game/state/playingState.go
Normal file
56
game/state/playingState.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/game/model"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PlayingState struct {
|
||||||
|
player *model.Player
|
||||||
|
|
||||||
|
pauseGame bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func BeginPlayingState() *PlayingState {
|
||||||
|
s := new(PlayingState)
|
||||||
|
|
||||||
|
s.player = model.CreatePlayer(10, 10, tcell.StyleDefault)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) Pause() {
|
||||||
|
ps.pauseGame = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) Unpause() {
|
||||||
|
ps.pauseGame = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) SetPaused(paused bool) {
|
||||||
|
ps.pauseGame = paused
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) OnInput(e *tcell.EventKey) {
|
||||||
|
ps.player.Input(e)
|
||||||
|
|
||||||
|
if e.Key() == tcell.KeyEsc {
|
||||||
|
ps.pauseGame = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) OnTick(dt int64) GameState {
|
||||||
|
ps.player.Tick(dt)
|
||||||
|
|
||||||
|
if ps.pauseGame {
|
||||||
|
return PauseGame(ps)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayingState) OnDraw(c views.View) {
|
||||||
|
ps.player.Draw(c)
|
||||||
|
}
|
21
game/state/quitState.go
Normal file
21
game/state/quitState.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QuitState struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QuitState) OnInput(e *tcell.EventKey) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QuitState) OnTick(dt int64) GameState {
|
||||||
|
return q
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *QuitState) OnDraw(c views.View) {
|
||||||
|
|
||||||
|
}
|
1
layer.go
1
layer.go
|
@ -1 +0,0 @@
|
||||||
package main
|
|
42
main.go
42
main.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"mvvasilev/last_light/game"
|
||||||
"mvvasilev/last_light/render"
|
"mvvasilev/last_light/render"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -18,46 +19,33 @@ func main() {
|
||||||
log.Fatalf("%~v", err)
|
log.Fatalf("%~v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g := game.CreateGame()
|
||||||
|
|
||||||
c.HandleInput(func(ev *tcell.EventKey) {
|
c.HandleInput(func(ev *tcell.EventKey) {
|
||||||
if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
|
if ev.Key() == tcell.KeyCtrlC {
|
||||||
c.Stop()
|
c.Stop()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g.Input(ev)
|
||||||
})
|
})
|
||||||
|
|
||||||
defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset)
|
defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset)
|
||||||
|
|
||||||
rect := render.CreateRectangle(
|
|
||||||
0, 0, 80, 24,
|
|
||||||
'┌', '─', '┐',
|
|
||||||
'│', '#', '│',
|
|
||||||
'└', '─', '┘',
|
|
||||||
false, true, defStyle,
|
|
||||||
)
|
|
||||||
|
|
||||||
// text := render.CreateText(1, 2, 8, 8, "Hello World! How are you today?", defStyle)
|
|
||||||
|
|
||||||
// grid := render.CreateGrid(
|
|
||||||
// 11, 1, 3, 3, 3, 3,
|
|
||||||
// '┌', '─', '┬', '┐',
|
|
||||||
// '│', '#', '│', '│',
|
|
||||||
// '├', '─', '┼', '┤',
|
|
||||||
// '└', '─', '┴', '┘',
|
|
||||||
// defStyle,
|
|
||||||
// )
|
|
||||||
|
|
||||||
layers := render.CreateLayeredDrawContainer()
|
|
||||||
|
|
||||||
layers.Insert(0, rect)
|
|
||||||
// layers.Insert(1, text)
|
|
||||||
// layers.Insert(0, grid)
|
|
||||||
|
|
||||||
c.HandleRender(func(view views.View, deltaTime int64) {
|
c.HandleRender(func(view views.View, deltaTime int64) {
|
||||||
fps := 1_000_000 / deltaTime
|
fps := 1_000_000 / deltaTime
|
||||||
|
|
||||||
fpsText := render.CreateText(0, 0, 16, 1, fmt.Sprintf("%v FPS", fps), defStyle)
|
fpsText := render.CreateText(0, 0, 16, 1, fmt.Sprintf("%v FPS", fps), defStyle)
|
||||||
|
|
||||||
layers.Draw(view)
|
keepGoing := g.Tick(deltaTime)
|
||||||
|
|
||||||
|
if !keepGoing {
|
||||||
|
c.Stop()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Draw(view)
|
||||||
|
|
||||||
fpsText.Draw(view)
|
fpsText.Draw(view)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ type Drawable interface {
|
||||||
Draw(v views.View)
|
Draw(v views.View)
|
||||||
}
|
}
|
||||||
|
|
||||||
type renderContext struct {
|
type RenderContext struct {
|
||||||
screen tcell.Screen
|
screen tcell.Screen
|
||||||
view *views.ViewPort
|
view *views.ViewPort
|
||||||
defaultStyle tcell.Style
|
defaultStyle tcell.Style
|
||||||
|
@ -36,7 +36,7 @@ type renderContext struct {
|
||||||
inputHandler func(ev *tcell.EventKey)
|
inputHandler func(ev *tcell.EventKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateRenderContext() (*renderContext, error) {
|
func CreateRenderContext() (*RenderContext, error) {
|
||||||
s, err := tcell.NewScreen()
|
s, err := tcell.NewScreen()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,7 +77,7 @@ func CreateRenderContext() (*renderContext, error) {
|
||||||
|
|
||||||
go s.ChannelEvents(events, quit)
|
go s.ChannelEvents(events, quit)
|
||||||
|
|
||||||
context := new(renderContext)
|
context := new(RenderContext)
|
||||||
|
|
||||||
context.screen = s
|
context.screen = s
|
||||||
context.defaultStyle = defStyle
|
context.defaultStyle = defStyle
|
||||||
|
@ -88,19 +88,19 @@ func CreateRenderContext() (*renderContext, error) {
|
||||||
return context, nil
|
return context, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *renderContext) Stop() {
|
func (c *RenderContext) Stop() {
|
||||||
c.screen.Fini()
|
c.screen.Fini()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *renderContext) HandleRender(renderHandler func(view views.View, deltaTime int64)) {
|
func (c *RenderContext) HandleRender(renderHandler func(view views.View, deltaTime int64)) {
|
||||||
c.renderHandler = renderHandler
|
c.renderHandler = renderHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *renderContext) HandleInput(inputHandler func(ev *tcell.EventKey)) {
|
func (c *RenderContext) HandleInput(inputHandler func(ev *tcell.EventKey)) {
|
||||||
c.inputHandler = inputHandler
|
c.inputHandler = inputHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *renderContext) onResize(ev *tcell.EventResize) {
|
func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
||||||
width, height := ev.Size()
|
width, height := ev.Size()
|
||||||
|
|
||||||
c.screen.Clear()
|
c.screen.Clear()
|
||||||
|
@ -115,7 +115,7 @@ func (c *renderContext) onResize(ev *tcell.EventResize) {
|
||||||
c.screen.Sync()
|
c.screen.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *renderContext) BeginRendering() {
|
func (c *RenderContext) BeginRendering() {
|
||||||
c.lastRenderTime = time.Now()
|
c.lastRenderTime = time.Now()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type grid struct {
|
type Grid struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
|
|
||||||
internalCellSize util.Size
|
internalCellSize util.Size
|
||||||
|
@ -45,7 +45,7 @@ func CreateSimpleGrid(
|
||||||
numCellsHorizontal, numCellsVertical uint16,
|
numCellsHorizontal, numCellsVertical uint16,
|
||||||
borderRune, fillRune rune,
|
borderRune, fillRune rune,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
) grid {
|
) Grid {
|
||||||
return CreateGrid(
|
return CreateGrid(
|
||||||
x, y, cellWidth, cellHeight, numCellsHorizontal, numCellsVertical,
|
x, y, cellWidth, cellHeight, numCellsHorizontal, numCellsVertical,
|
||||||
borderRune, borderRune, borderRune, borderRune,
|
borderRune, borderRune, borderRune, borderRune,
|
||||||
|
@ -72,8 +72,8 @@ func CreateGrid(
|
||||||
horizontalRightTJunction, internalHorizontalBorder, crossJunction, horizontalLeftTJunction,
|
horizontalRightTJunction, internalHorizontalBorder, crossJunction, horizontalLeftTJunction,
|
||||||
swCorner, southBorder, verticalUpwardsTJunction, seCorner rune,
|
swCorner, southBorder, verticalUpwardsTJunction, seCorner rune,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
) grid {
|
) Grid {
|
||||||
return grid{
|
return Grid{
|
||||||
id: uuid.New(),
|
id: uuid.New(),
|
||||||
internalCellSize: util.SizeOf(cellWidth, cellHeight),
|
internalCellSize: util.SizeOf(cellWidth, cellHeight),
|
||||||
numCellsHorizontal: numCellsHorizontal,
|
numCellsHorizontal: numCellsHorizontal,
|
||||||
|
@ -100,7 +100,7 @@ func CreateGrid(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g grid) UniqueId() uuid.UUID {
|
func (g Grid) UniqueId() uuid.UUID {
|
||||||
return g.id
|
return g.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,9 +117,11 @@ func (g grid) UniqueId() uuid.UUID {
|
||||||
// # # # #
|
// # # # #
|
||||||
// # # # #
|
// # # # #
|
||||||
// C###T###T###C
|
// C###T###T###C
|
||||||
func (g grid) drawBorders(v views.View) {
|
func (g Grid) drawBorders(v views.View) {
|
||||||
width := 2 + (g.internalCellSize.Width() * int(g.numCellsHorizontal)) + (int(g.numCellsHorizontal) - 1)
|
iCellSizeWidth := g.internalCellSize.Width()
|
||||||
height := 2 + (g.internalCellSize.Height() * int(g.numCellsVertical)) + (int(g.numCellsVertical) - 1)
|
iCellSizeHeight := g.internalCellSize.Height()
|
||||||
|
width := 1 + (iCellSizeWidth * int(g.numCellsHorizontal)) + (int(g.numCellsHorizontal))
|
||||||
|
height := 1 + (iCellSizeHeight * int(g.numCellsVertical)) + (int(g.numCellsVertical))
|
||||||
x := g.position.X()
|
x := g.position.X()
|
||||||
y := g.position.Y()
|
y := g.position.Y()
|
||||||
|
|
||||||
|
@ -128,22 +130,54 @@ func (g grid) drawBorders(v views.View) {
|
||||||
v.SetContent(x, y+height-1, g.swCorner, nil, g.style)
|
v.SetContent(x, y+height-1, g.swCorner, nil, g.style)
|
||||||
v.SetContent(x+width-1, y+height-1, g.seCorner, nil, g.style)
|
v.SetContent(x+width-1, y+height-1, g.seCorner, nil, g.style)
|
||||||
|
|
||||||
for w := range width - 2 {
|
for w := 1; w < width-1; w++ {
|
||||||
v.SetContent(1+w, y, g.northBorder, nil, g.style)
|
|
||||||
v.SetContent(1+w, y+height-1, g.southBorder, nil, g.style)
|
for iw := 1; iw < int(g.numCellsVertical); iw++ {
|
||||||
|
if w%(iCellSizeWidth+1) == 0 {
|
||||||
|
v.SetContent(x+w, y+(iw*iCellSizeHeight+iw), g.crossJunction, nil, g.style)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for h := range height - 2 {
|
v.SetContent(x+w, y+(iw*iCellSizeHeight+iw), g.internalHorizontalBorder, nil, g.style)
|
||||||
v.SetContent(x, 1+h, g.westBorder, nil, g.style)
|
}
|
||||||
v.SetContent(x+width-1, 1+h, g.eastBorder, nil, g.style)
|
|
||||||
|
if w%(iCellSizeWidth+1) == 0 {
|
||||||
|
v.SetContent(x+w, y, g.verticalDownwardsTJunction, nil, g.style)
|
||||||
|
v.SetContent(x+w, y+height-1, g.verticalUpwardsTJunction, nil, g.style)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetContent(x+w, y, g.northBorder, nil, g.style)
|
||||||
|
v.SetContent(x+w, y+height-1, g.southBorder, nil, g.style)
|
||||||
|
}
|
||||||
|
|
||||||
|
for h := 1; h < height-1; h++ {
|
||||||
|
|
||||||
|
for ih := 1; ih < int(g.numCellsHorizontal); ih++ {
|
||||||
|
if h%(iCellSizeHeight+1) == 0 {
|
||||||
|
v.SetContent(x+(ih*iCellSizeHeight+ih), y+h, g.crossJunction, nil, g.style)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetContent(x+(ih*iCellSizeHeight+ih), y+h, g.internalVerticalBorder, nil, g.style)
|
||||||
|
}
|
||||||
|
|
||||||
|
if h%(iCellSizeHeight+1) == 0 {
|
||||||
|
v.SetContent(x, y+h, g.horizontalRightTJunction, nil, g.style)
|
||||||
|
v.SetContent(x+width-1, y+h, g.horizontalLeftTJunction, nil, g.style)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
v.SetContent(x, y+h, g.westBorder, nil, g.style)
|
||||||
|
v.SetContent(x+width-1, y+h, g.eastBorder, nil, g.style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g grid) drawFill(v views.View) {
|
func (g Grid) drawFill(v views.View) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g grid) Draw(v views.View) {
|
func (g Grid) Draw(v views.View) {
|
||||||
g.drawBorders(v)
|
g.drawBorders(v)
|
||||||
g.drawFill(v)
|
g.drawFill(v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,13 +37,13 @@ func (l *layer) draw(s views.View) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type unorderedDrawContainer struct {
|
type UnorderedDrawContainer struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
contents []Drawable
|
contents []Drawable
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateUnorderedDrawContainer(contents []Drawable) unorderedDrawContainer {
|
func CreateUnorderedDrawContainer(contents []Drawable) UnorderedDrawContainer {
|
||||||
return unorderedDrawContainer{
|
return UnorderedDrawContainer{
|
||||||
id: uuid.New(),
|
id: uuid.New(),
|
||||||
contents: contents,
|
contents: contents,
|
||||||
}
|
}
|
||||||
|
|
46
render/raw.go
Normal file
46
render/raw.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Raw struct {
|
||||||
|
id uuid.UUID
|
||||||
|
buffer [][]rune
|
||||||
|
position util.Position
|
||||||
|
style tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRawDrawable(x, y uint16, style tcell.Style, buffer ...string) *Raw {
|
||||||
|
r := new(Raw)
|
||||||
|
|
||||||
|
r.position = util.PositionAt(x, y)
|
||||||
|
r.buffer = make([][]rune, 0)
|
||||||
|
|
||||||
|
for _, row := range buffer {
|
||||||
|
r.buffer = append(r.buffer, []rune(row))
|
||||||
|
}
|
||||||
|
|
||||||
|
r.style = style
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Raw) UniqueId() uuid.UUID {
|
||||||
|
return r.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Raw) Draw(v views.View) {
|
||||||
|
x := r.position.X()
|
||||||
|
y := r.position.Y()
|
||||||
|
|
||||||
|
for h, row := range r.buffer {
|
||||||
|
for i, ru := range row {
|
||||||
|
v.SetContent(x+i, y+h, ru, nil, r.style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type rectangle struct {
|
type Rectangle struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
|
|
||||||
size util.Size
|
size util.Size
|
||||||
|
@ -31,7 +31,7 @@ type rectangle struct {
|
||||||
fillRune rune
|
fillRune rune
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateBorderlessRectangle(x, y uint16, width, height uint16, fillRune rune, style tcell.Style) rectangle {
|
func CreateBorderlessRectangle(x, y uint16, width, height uint16, fillRune rune, style tcell.Style) Rectangle {
|
||||||
return CreateRectangle(
|
return CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
|
@ -41,7 +41,7 @@ func CreateBorderlessRectangle(x, y uint16, width, height uint16, fillRune rune,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSimpleEmptyRectangle(x, y uint16, width, height uint16, borderRune rune, style tcell.Style) rectangle {
|
func CreateSimpleEmptyRectangle(x, y uint16, width, height uint16, borderRune rune, style tcell.Style) Rectangle {
|
||||||
return CreateRectangle(
|
return CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
borderRune, borderRune, borderRune,
|
borderRune, borderRune, borderRune,
|
||||||
|
@ -51,7 +51,7 @@ func CreateSimpleEmptyRectangle(x, y uint16, width, height uint16, borderRune ru
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateSimpleRectangle(x uint16, y uint16, width uint16, height uint16, borderRune rune, fillRune rune, style tcell.Style) rectangle {
|
func CreateSimpleRectangle(x uint16, y uint16, width uint16, height uint16, borderRune rune, fillRune rune, style tcell.Style) Rectangle {
|
||||||
return CreateRectangle(
|
return CreateRectangle(
|
||||||
x, y, width, height,
|
x, y, width, height,
|
||||||
borderRune, borderRune, borderRune,
|
borderRune, borderRune, borderRune,
|
||||||
|
@ -61,6 +61,25 @@ func CreateSimpleRectangle(x uint16, y uint16, width uint16, height uint16, bord
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateRectangleV2(
|
||||||
|
x, y uint16, width, height uint16,
|
||||||
|
upper, middle, lower string,
|
||||||
|
isBorderless, isFilled bool,
|
||||||
|
style tcell.Style,
|
||||||
|
) Rectangle {
|
||||||
|
upperRunes := []rune(upper)
|
||||||
|
middleRunes := []rune(middle)
|
||||||
|
lowerRunes := []rune(lower)
|
||||||
|
|
||||||
|
return CreateRectangle(
|
||||||
|
x, y, width, height,
|
||||||
|
upperRunes[0], upperRunes[1], upperRunes[2],
|
||||||
|
middleRunes[0], middleRunes[1], middleRunes[2],
|
||||||
|
lowerRunes[0], lowerRunes[1], lowerRunes[2],
|
||||||
|
isBorderless, isFilled, style,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateRectangle(
|
// CreateRectangle(
|
||||||
//
|
//
|
||||||
// x, y, width, height,
|
// x, y, width, height,
|
||||||
|
@ -81,8 +100,8 @@ func CreateRectangle(
|
||||||
swCorner, southBorder, seCorner rune,
|
swCorner, southBorder, seCorner rune,
|
||||||
isBorderless, isFilled bool,
|
isBorderless, isFilled bool,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
) rectangle {
|
) Rectangle {
|
||||||
return rectangle{
|
return Rectangle{
|
||||||
id: uuid.New(),
|
id: uuid.New(),
|
||||||
size: util.SizeOf(width, height),
|
size: util.SizeOf(width, height),
|
||||||
position: util.PositionAt(x, y),
|
position: util.PositionAt(x, y),
|
||||||
|
@ -101,11 +120,15 @@ func CreateRectangle(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rect rectangle) UniqueId() uuid.UUID {
|
func (rect Rectangle) UniqueId() uuid.UUID {
|
||||||
return rect.id
|
return rect.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rect rectangle) drawBorders(v views.View) {
|
func (rect Rectangle) Position() util.Position {
|
||||||
|
return rect.position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rect Rectangle) drawBorders(v views.View) {
|
||||||
width := rect.size.Width()
|
width := rect.size.Width()
|
||||||
height := rect.size.Height()
|
height := rect.size.Height()
|
||||||
x := rect.position.X()
|
x := rect.position.X()
|
||||||
|
@ -116,26 +139,31 @@ func (rect rectangle) drawBorders(v views.View) {
|
||||||
v.SetContent(x, y+height-1, rect.swCorner, nil, rect.style)
|
v.SetContent(x, y+height-1, rect.swCorner, nil, rect.style)
|
||||||
v.SetContent(x+width-1, y+height-1, rect.seCorner, nil, rect.style)
|
v.SetContent(x+width-1, y+height-1, rect.seCorner, nil, rect.style)
|
||||||
|
|
||||||
for w := range width - 2 {
|
for w := 1; w < width-1; w++ {
|
||||||
v.SetContent(1+w, y, rect.northBorder, nil, rect.style)
|
v.SetContent(x+w, y, rect.northBorder, nil, rect.style)
|
||||||
v.SetContent(1+w, y+height-1, rect.southBorder, nil, rect.style)
|
v.SetContent(x+w, y+height-1, rect.southBorder, nil, rect.style)
|
||||||
}
|
}
|
||||||
|
|
||||||
for h := range height - 2 {
|
for h := 1; h < height-1; h++ {
|
||||||
v.SetContent(x, 1+h, rect.westBorder, nil, rect.style)
|
v.SetContent(x, y+h, rect.westBorder, nil, rect.style)
|
||||||
v.SetContent(x+width-1, 1+h, rect.eastBorder, nil, rect.style)
|
v.SetContent(x+width-1, y+h, rect.eastBorder, nil, rect.style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rect rectangle) drawFill(v views.View) {
|
func (rect Rectangle) drawFill(v views.View) {
|
||||||
for w := range rect.size.Width() - 2 {
|
width := rect.size.Width()
|
||||||
for h := range rect.size.Height() - 2 {
|
height := rect.size.Height()
|
||||||
v.SetContent(1+w, 1+h, rect.fillRune, nil, rect.style)
|
x := rect.position.X()
|
||||||
|
y := rect.position.Y()
|
||||||
|
|
||||||
|
for w := 1; w < width-1; w++ {
|
||||||
|
for h := 1; h < height-1; h++ {
|
||||||
|
v.SetContent(x+w, y+h, rect.fillRune, nil, rect.style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rect rectangle) Draw(v views.View) {
|
func (rect Rectangle) Draw(v views.View) {
|
||||||
if !rect.isBorderless {
|
if !rect.isBorderless {
|
||||||
rect.drawBorders(v)
|
rect.drawBorders(v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type text struct {
|
type Text struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
content []string
|
content []string
|
||||||
position util.Position
|
position util.Position
|
||||||
|
@ -23,21 +23,43 @@ func CreateText(
|
||||||
width, height uint16,
|
width, height uint16,
|
||||||
content string,
|
content string,
|
||||||
style tcell.Style,
|
style tcell.Style,
|
||||||
) text {
|
) *Text {
|
||||||
return text{
|
text := new(Text)
|
||||||
id: uuid.New(),
|
|
||||||
content: strings.Split(content, " "),
|
text.id = uuid.New()
|
||||||
style: style,
|
text.content = strings.Split(content, " ")
|
||||||
size: util.SizeOf(width, height),
|
text.style = style
|
||||||
position: util.PositionAt(x, y),
|
text.size = util.SizeOf(width, height)
|
||||||
}
|
text.position = util.PositionAt(x, y)
|
||||||
|
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t text) UniqueId() uuid.UUID {
|
func (t *Text) UniqueId() uuid.UUID {
|
||||||
return t.id
|
return t.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t text) Draw(s views.View) {
|
func (t *Text) Position() util.Position {
|
||||||
|
return t.position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Text) Content() string {
|
||||||
|
return strings.Join(t.content, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Text) Size() util.Size {
|
||||||
|
return t.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Text) SetStyle(style tcell.Style) {
|
||||||
|
t.style = style
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Text) Style() tcell.Style {
|
||||||
|
return t.style
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Text) Draw(s views.View) {
|
||||||
width := t.size.Width()
|
width := t.size.Width()
|
||||||
height := t.size.Height()
|
height := t.size.Height()
|
||||||
x := t.position.X()
|
x := t.position.X()
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
package main
|
|
62
ui/borderButton.go
Normal file
62
ui/borderButton.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIBorderedButton struct {
|
||||||
|
id uuid.UUID
|
||||||
|
|
||||||
|
text render.Text
|
||||||
|
border render.Rectangle
|
||||||
|
|
||||||
|
isSelected bool
|
||||||
|
|
||||||
|
unselectedStyle tcell.Style
|
||||||
|
selectedStyle tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) IsSelected() bool {
|
||||||
|
return b.isSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) Select() {
|
||||||
|
b.isSelected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) Deselect() {
|
||||||
|
b.isSelected = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) SetSelected(selected bool) {
|
||||||
|
b.isSelected = selected
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) UniqueId() uuid.UUID {
|
||||||
|
return b.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) MoveTo(x uint16, y uint16) {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) Position() util.Position {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) Size() util.Size {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) Draw(v views.View) {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UIBorderedButton) Input(e *tcell.EventKey) {
|
||||||
|
panic("not implemented") // TODO: Implement
|
||||||
|
}
|
82
ui/container.go
Normal file
82
ui/container.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIContainerLayout int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// These change the provided ui positions
|
||||||
|
UpperLeft UIContainerLayout = iota
|
||||||
|
MiddleLeft
|
||||||
|
LowerLeft
|
||||||
|
UpperRight
|
||||||
|
MiddleRight
|
||||||
|
LowerRight
|
||||||
|
UpperCenter
|
||||||
|
MiddleCenter
|
||||||
|
LowerCenter
|
||||||
|
// This uses the positions as provided in the ui elements
|
||||||
|
Manual
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIContainer struct {
|
||||||
|
id uuid.UUID
|
||||||
|
layout UIContainerLayout
|
||||||
|
position util.Position
|
||||||
|
size util.Size
|
||||||
|
elements []UIElement
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUIContainer(x, y uint16, width, height uint16, layout UIContainerLayout) *UIContainer {
|
||||||
|
container := new(UIContainer)
|
||||||
|
|
||||||
|
container.id = uuid.New()
|
||||||
|
container.layout = layout
|
||||||
|
container.position = util.PositionAt(x, y)
|
||||||
|
container.size = util.SizeOf(width, height)
|
||||||
|
container.elements = make([]UIElement, 0)
|
||||||
|
|
||||||
|
return container
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) Push(element UIElement) {
|
||||||
|
uic.elements = append(uic.elements, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) Clear() {
|
||||||
|
uic.elements = make([]UIElement, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) UniqueId() uuid.UUID {
|
||||||
|
return uic.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) MoveTo(x, y uint16) {
|
||||||
|
uic.position = util.PositionAt(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) Position() util.Position {
|
||||||
|
return uic.position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) Size() util.Size {
|
||||||
|
return uic.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) Draw(v views.View) {
|
||||||
|
for _, e := range uic.elements {
|
||||||
|
e.Draw(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uic *UIContainer) Input(ev *tcell.EventKey) {
|
||||||
|
for _, e := range uic.elements {
|
||||||
|
e.Input(ev)
|
||||||
|
}
|
||||||
|
}
|
56
ui/label.go
Normal file
56
ui/label.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UILabel struct {
|
||||||
|
id uuid.UUID
|
||||||
|
text *render.Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUILabel(x, y uint16, width, height uint16, content string, style tcell.Style) *UILabel {
|
||||||
|
label := new(UILabel)
|
||||||
|
|
||||||
|
label.id = uuid.New()
|
||||||
|
label.text = render.CreateText(x, y, width, height, content, style)
|
||||||
|
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSingleLineUILabel(x, y uint16, content string, style tcell.Style) *UILabel {
|
||||||
|
label := new(UILabel)
|
||||||
|
|
||||||
|
label.id = uuid.New()
|
||||||
|
label.text = render.CreateText(x, y, uint16(utf8.RuneCountInString(content)), 1, content, style)
|
||||||
|
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UILabel) UniqueId() uuid.UUID {
|
||||||
|
return t.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UILabel) MoveTo(x uint16, y uint16) {
|
||||||
|
t.text = render.CreateText(x, y, uint16(t.text.Size().Width()), uint16(t.Size().Height()), t.text.Content(), t.text.Style())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UILabel) Position() util.Position {
|
||||||
|
return t.text.Position()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UILabel) Size() util.Size {
|
||||||
|
return t.text.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UILabel) Draw(v views.View) {
|
||||||
|
t.text.Draw(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *UILabel) Input(e *tcell.EventKey) {}
|
102
ui/simpleButton.go
Normal file
102
ui/simpleButton.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UISimpleButton struct {
|
||||||
|
id uuid.UUID
|
||||||
|
isHighlighted bool
|
||||||
|
text *render.Text
|
||||||
|
selectHandler func()
|
||||||
|
unhighlightedStyle tcell.Style
|
||||||
|
highlightedStyle tcell.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSimpleButton(x, y uint16, text string, unhighlightedStyle, highlightedStyle tcell.Style, onSelect func()) *UISimpleButton {
|
||||||
|
sb := new(UISimpleButton)
|
||||||
|
|
||||||
|
sb.id = uuid.New()
|
||||||
|
sb.text = render.CreateText(x, y, uint16(utf8.RuneCountInString(text)), 1, text, unhighlightedStyle)
|
||||||
|
sb.isHighlighted = false
|
||||||
|
sb.selectHandler = onSelect
|
||||||
|
sb.highlightedStyle = highlightedStyle
|
||||||
|
sb.unhighlightedStyle = unhighlightedStyle
|
||||||
|
|
||||||
|
return sb
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Select() {
|
||||||
|
sb.selectHandler()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) OnSelect(f func()) {
|
||||||
|
sb.selectHandler = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) IsHighlighted() bool {
|
||||||
|
return sb.isHighlighted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Highlight() {
|
||||||
|
sb.isHighlighted = true
|
||||||
|
|
||||||
|
newContent := "[ " + sb.text.Content() + " ]"
|
||||||
|
|
||||||
|
sb.text = render.CreateText(
|
||||||
|
uint16(sb.Position().X()-2), uint16(sb.Position().Y()),
|
||||||
|
uint16(utf8.RuneCountInString(newContent)), 1,
|
||||||
|
newContent,
|
||||||
|
sb.highlightedStyle,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Unhighlight() {
|
||||||
|
sb.isHighlighted = false
|
||||||
|
|
||||||
|
content := strings.Trim(sb.text.Content(), " ]")
|
||||||
|
content = strings.Trim(content, "[ ")
|
||||||
|
contentLen := utf8.RuneCountInString(content)
|
||||||
|
|
||||||
|
sb.text = render.CreateText(
|
||||||
|
uint16(sb.Position().X()+2), uint16(sb.Position().Y()),
|
||||||
|
uint16(contentLen), 1,
|
||||||
|
content,
|
||||||
|
sb.unhighlightedStyle,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) SetHighlighted(highlighted bool) {
|
||||||
|
sb.isHighlighted = highlighted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) UniqueId() uuid.UUID {
|
||||||
|
return sb.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) MoveTo(x uint16, y uint16) {
|
||||||
|
sb.text = render.CreateText(x, y, uint16(utf8.RuneCountInString(sb.text.Content())), 1, sb.text.Content(), sb.highlightedStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Position() util.Position {
|
||||||
|
return sb.text.Position()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Size() util.Size {
|
||||||
|
return sb.text.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Draw(v views.View) {
|
||||||
|
sb.text.Draw(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *UISimpleButton) Input(e *tcell.EventKey) {
|
||||||
|
|
||||||
|
}
|
20
ui/ui.go
20
ui/ui.go
|
@ -3,10 +3,30 @@ package ui
|
||||||
import (
|
import (
|
||||||
"mvvasilev/last_light/util"
|
"mvvasilev/last_light/util"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UIElement interface {
|
type UIElement interface {
|
||||||
UniqueId() uuid.UUID
|
UniqueId() uuid.UUID
|
||||||
|
MoveTo(x, y uint16)
|
||||||
Position() util.Position
|
Position() util.Position
|
||||||
|
Size() util.Size
|
||||||
|
Draw(v views.View)
|
||||||
|
Input(e *tcell.EventKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UIHighlightableElement interface {
|
||||||
|
IsHighlighted() bool
|
||||||
|
Highlight()
|
||||||
|
Unhighlight()
|
||||||
|
SetHighlighted(highlighted bool)
|
||||||
|
UIElement
|
||||||
|
}
|
||||||
|
|
||||||
|
type UISelectableElement interface {
|
||||||
|
Select()
|
||||||
|
OnSelect(func())
|
||||||
|
UIHighlightableElement
|
||||||
}
|
}
|
||||||
|
|
62
ui/window.go
Normal file
62
ui/window.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mvvasilev/last_light/render"
|
||||||
|
"mvvasilev/last_light/util"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell/v2"
|
||||||
|
"github.com/gdamore/tcell/v2/views"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UIWindow struct {
|
||||||
|
id uuid.UUID
|
||||||
|
|
||||||
|
title *render.Text
|
||||||
|
box render.Rectangle
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWindow(x, y, width, height uint16, title string, style tcell.Style) *UIWindow {
|
||||||
|
w := new(UIWindow)
|
||||||
|
|
||||||
|
titleLen := utf8.RuneCountInString(title)
|
||||||
|
|
||||||
|
titlePos := (width / 2) - uint16(titleLen/2)
|
||||||
|
|
||||||
|
w.title = render.CreateText(x+titlePos, y, uint16(titleLen), 1, title, style)
|
||||||
|
|
||||||
|
w.box = render.CreateRectangle(
|
||||||
|
x, y, width, height,
|
||||||
|
'┌', '─', '┐',
|
||||||
|
'│', ' ', '│',
|
||||||
|
'└', '─', '┘',
|
||||||
|
false, true, style,
|
||||||
|
)
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UIWindow) UniqueId() uuid.UUID {
|
||||||
|
return w.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UIWindow) MoveTo(x uint16, y uint16) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UIWindow) Position() util.Position {
|
||||||
|
return w.box.Position()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UIWindow) Size() util.Size {
|
||||||
|
return util.SizeOf(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UIWindow) Draw(v views.View) {
|
||||||
|
w.box.Draw(v)
|
||||||
|
w.title.Draw(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *UIWindow) Input(e *tcell.EventKey) {
|
||||||
|
}
|
28
util/util.go
28
util/util.go
|
@ -17,6 +17,14 @@ func (p Position) Y() int {
|
||||||
return p.y
|
return p.y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Position) XY() (int, int) {
|
||||||
|
return p.x, p.y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Position) XYUint16() (uint16, uint16) {
|
||||||
|
return uint16(p.x), uint16(p.y)
|
||||||
|
}
|
||||||
|
|
||||||
type Size struct {
|
type Size struct {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
@ -33,3 +41,23 @@ func (s Size) Width() int {
|
||||||
func (s Size) Height() int {
|
func (s Size) Height() int {
|
||||||
return s.height
|
return s.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Size) WHUint16() (uint16, uint16) {
|
||||||
|
return uint16(s.width), uint16(s.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LimitIncrement(i int, limit int) int {
|
||||||
|
if (i + 1) > limit {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func LimitDecrement(i int, limit int) int {
|
||||||
|
if (i - 1) < limit {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return i - 1
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue