mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-04-18 20:29: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 (
|
||||
"fmt"
|
||||
"log"
|
||||
"mvvasilev/last_light/game"
|
||||
"mvvasilev/last_light/render"
|
||||
"os"
|
||||
|
||||
|
@ -18,46 +19,33 @@ func main() {
|
|||
log.Fatalf("%~v", err)
|
||||
}
|
||||
|
||||
g := game.CreateGame()
|
||||
|
||||
c.HandleInput(func(ev *tcell.EventKey) {
|
||||
if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC {
|
||||
if ev.Key() == tcell.KeyCtrlC {
|
||||
c.Stop()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
g.Input(ev)
|
||||
})
|
||||
|
||||
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) {
|
||||
fps := 1_000_000 / deltaTime
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ type Drawable interface {
|
|||
Draw(v views.View)
|
||||
}
|
||||
|
||||
type renderContext struct {
|
||||
type RenderContext struct {
|
||||
screen tcell.Screen
|
||||
view *views.ViewPort
|
||||
defaultStyle tcell.Style
|
||||
|
@ -36,7 +36,7 @@ type renderContext struct {
|
|||
inputHandler func(ev *tcell.EventKey)
|
||||
}
|
||||
|
||||
func CreateRenderContext() (*renderContext, error) {
|
||||
func CreateRenderContext() (*RenderContext, error) {
|
||||
s, err := tcell.NewScreen()
|
||||
|
||||
if err != nil {
|
||||
|
@ -77,7 +77,7 @@ func CreateRenderContext() (*renderContext, error) {
|
|||
|
||||
go s.ChannelEvents(events, quit)
|
||||
|
||||
context := new(renderContext)
|
||||
context := new(RenderContext)
|
||||
|
||||
context.screen = s
|
||||
context.defaultStyle = defStyle
|
||||
|
@ -88,19 +88,19 @@ func CreateRenderContext() (*renderContext, error) {
|
|||
return context, nil
|
||||
}
|
||||
|
||||
func (c *renderContext) Stop() {
|
||||
func (c *RenderContext) Stop() {
|
||||
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
|
||||
}
|
||||
|
||||
func (c *renderContext) HandleInput(inputHandler func(ev *tcell.EventKey)) {
|
||||
func (c *RenderContext) HandleInput(inputHandler func(ev *tcell.EventKey)) {
|
||||
c.inputHandler = inputHandler
|
||||
}
|
||||
|
||||
func (c *renderContext) onResize(ev *tcell.EventResize) {
|
||||
func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
||||
width, height := ev.Size()
|
||||
|
||||
c.screen.Clear()
|
||||
|
@ -115,7 +115,7 @@ func (c *renderContext) onResize(ev *tcell.EventResize) {
|
|||
c.screen.Sync()
|
||||
}
|
||||
|
||||
func (c *renderContext) BeginRendering() {
|
||||
func (c *RenderContext) BeginRendering() {
|
||||
c.lastRenderTime = time.Now()
|
||||
|
||||
for {
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type grid struct {
|
||||
type Grid struct {
|
||||
id uuid.UUID
|
||||
|
||||
internalCellSize util.Size
|
||||
|
@ -45,7 +45,7 @@ func CreateSimpleGrid(
|
|||
numCellsHorizontal, numCellsVertical uint16,
|
||||
borderRune, fillRune rune,
|
||||
style tcell.Style,
|
||||
) grid {
|
||||
) Grid {
|
||||
return CreateGrid(
|
||||
x, y, cellWidth, cellHeight, numCellsHorizontal, numCellsVertical,
|
||||
borderRune, borderRune, borderRune, borderRune,
|
||||
|
@ -72,8 +72,8 @@ func CreateGrid(
|
|||
horizontalRightTJunction, internalHorizontalBorder, crossJunction, horizontalLeftTJunction,
|
||||
swCorner, southBorder, verticalUpwardsTJunction, seCorner rune,
|
||||
style tcell.Style,
|
||||
) grid {
|
||||
return grid{
|
||||
) Grid {
|
||||
return Grid{
|
||||
id: uuid.New(),
|
||||
internalCellSize: util.SizeOf(cellWidth, cellHeight),
|
||||
numCellsHorizontal: numCellsHorizontal,
|
||||
|
@ -100,7 +100,7 @@ func CreateGrid(
|
|||
}
|
||||
}
|
||||
|
||||
func (g grid) UniqueId() uuid.UUID {
|
||||
func (g Grid) UniqueId() uuid.UUID {
|
||||
return g.id
|
||||
}
|
||||
|
||||
|
@ -117,9 +117,11 @@ func (g grid) UniqueId() uuid.UUID {
|
|||
// # # # #
|
||||
// # # # #
|
||||
// C###T###T###C
|
||||
func (g grid) drawBorders(v views.View) {
|
||||
width := 2 + (g.internalCellSize.Width() * int(g.numCellsHorizontal)) + (int(g.numCellsHorizontal) - 1)
|
||||
height := 2 + (g.internalCellSize.Height() * int(g.numCellsVertical)) + (int(g.numCellsVertical) - 1)
|
||||
func (g Grid) drawBorders(v views.View) {
|
||||
iCellSizeWidth := g.internalCellSize.Width()
|
||||
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()
|
||||
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+width-1, y+height-1, g.seCorner, nil, g.style)
|
||||
|
||||
for w := range width - 2 {
|
||||
v.SetContent(1+w, y, g.northBorder, nil, g.style)
|
||||
v.SetContent(1+w, y+height-1, g.southBorder, nil, g.style)
|
||||
for w := 1; w < width-1; w++ {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
v.SetContent(x+w, y+(iw*iCellSizeHeight+iw), g.internalHorizontalBorder, 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 := range height - 2 {
|
||||
v.SetContent(x, 1+h, g.westBorder, nil, g.style)
|
||||
v.SetContent(x+width-1, 1+h, g.eastBorder, 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.drawFill(v)
|
||||
}
|
||||
|
|
|
@ -37,13 +37,13 @@ func (l *layer) draw(s views.View) {
|
|||
}
|
||||
}
|
||||
|
||||
type unorderedDrawContainer struct {
|
||||
type UnorderedDrawContainer struct {
|
||||
id uuid.UUID
|
||||
contents []Drawable
|
||||
}
|
||||
|
||||
func CreateUnorderedDrawContainer(contents []Drawable) unorderedDrawContainer {
|
||||
return unorderedDrawContainer{
|
||||
func CreateUnorderedDrawContainer(contents []Drawable) UnorderedDrawContainer {
|
||||
return UnorderedDrawContainer{
|
||||
id: uuid.New(),
|
||||
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"
|
||||
)
|
||||
|
||||
type rectangle struct {
|
||||
type Rectangle struct {
|
||||
id uuid.UUID
|
||||
|
||||
size util.Size
|
||||
|
@ -31,7 +31,7 @@ type rectangle struct {
|
|||
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(
|
||||
x, y, width, height,
|
||||
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(
|
||||
x, y, width, height,
|
||||
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(
|
||||
x, y, width, height,
|
||||
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(
|
||||
//
|
||||
// x, y, width, height,
|
||||
|
@ -81,8 +100,8 @@ func CreateRectangle(
|
|||
swCorner, southBorder, seCorner rune,
|
||||
isBorderless, isFilled bool,
|
||||
style tcell.Style,
|
||||
) rectangle {
|
||||
return rectangle{
|
||||
) Rectangle {
|
||||
return Rectangle{
|
||||
id: uuid.New(),
|
||||
size: util.SizeOf(width, height),
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
height := rect.size.Height()
|
||||
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+width-1, y+height-1, rect.seCorner, nil, rect.style)
|
||||
|
||||
for w := range width - 2 {
|
||||
v.SetContent(1+w, y, rect.northBorder, nil, rect.style)
|
||||
v.SetContent(1+w, y+height-1, rect.southBorder, nil, rect.style)
|
||||
for w := 1; w < width-1; w++ {
|
||||
v.SetContent(x+w, y, rect.northBorder, nil, rect.style)
|
||||
v.SetContent(x+w, y+height-1, rect.southBorder, nil, rect.style)
|
||||
}
|
||||
|
||||
for h := range height - 2 {
|
||||
v.SetContent(x, 1+h, rect.westBorder, nil, rect.style)
|
||||
v.SetContent(x+width-1, 1+h, rect.eastBorder, nil, rect.style)
|
||||
for h := 1; h < height-1; h++ {
|
||||
v.SetContent(x, y+h, rect.westBorder, nil, rect.style)
|
||||
v.SetContent(x+width-1, y+h, rect.eastBorder, nil, rect.style)
|
||||
}
|
||||
}
|
||||
|
||||
func (rect rectangle) drawFill(v views.View) {
|
||||
for w := range rect.size.Width() - 2 {
|
||||
for h := range rect.size.Height() - 2 {
|
||||
v.SetContent(1+w, 1+h, rect.fillRune, nil, rect.style)
|
||||
func (rect Rectangle) drawFill(v views.View) {
|
||||
width := rect.size.Width()
|
||||
height := rect.size.Height()
|
||||
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 {
|
||||
rect.drawBorders(v)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type text struct {
|
||||
type Text struct {
|
||||
id uuid.UUID
|
||||
content []string
|
||||
position util.Position
|
||||
|
@ -23,21 +23,43 @@ func CreateText(
|
|||
width, height uint16,
|
||||
content string,
|
||||
style tcell.Style,
|
||||
) text {
|
||||
return text{
|
||||
id: uuid.New(),
|
||||
content: strings.Split(content, " "),
|
||||
style: style,
|
||||
size: util.SizeOf(width, height),
|
||||
position: util.PositionAt(x, y),
|
||||
}
|
||||
) *Text {
|
||||
text := new(Text)
|
||||
|
||||
text.id = uuid.New()
|
||||
text.content = strings.Split(content, " ")
|
||||
text.style = style
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
height := t.size.Height()
|
||||
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 (
|
||||
"mvvasilev/last_light/util"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/gdamore/tcell/v2/views"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type UIElement interface {
|
||||
UniqueId() uuid.UUID
|
||||
MoveTo(x, y uint16)
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
width int
|
||||
height int
|
||||
|
@ -33,3 +41,23 @@ func (s Size) Width() int {
|
|||
func (s Size) Height() int {
|
||||
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