Move to a new state when menu option selected

This commit is contained in:
Logan Hunt 2023-01-04 15:22:28 -07:00
parent 93ae544684
commit 1536d0192f
No known key found for this signature in database
GPG Key ID: 8AC6A4B840C0EC49
4 changed files with 86 additions and 36 deletions

View File

@ -1,6 +1,6 @@
defmodule Chessh.SSH.Client do defmodule Chessh.SSH.Client do
alias IO.ANSI alias IO.ANSI
alias Chessh.SSH.Client.Menu alias Chessh.SSH.Client.{Menu, Board}
require Logger require Logger
use GenServer use GenServer
@ -98,6 +98,7 @@ defmodule Chessh.SSH.Client do
"\e[B" -> :down "\e[B" -> :down
"\e[D" -> :left "\e[D" -> :left
"\e[C" -> :right "\e[C" -> :right
"\r" -> :return
x -> x x -> x
end end
end end
@ -107,8 +108,8 @@ defmodule Chessh.SSH.Client do
Enum.member?(@min_terminal_height..@max_terminal_height, height) Enum.member?(@min_terminal_height..@max_terminal_height, height)
end end
defp render( def render(
%State{width: width, height: height, state_stack: [{module, _screen_state}]} = state %State{width: width, height: height, state_stack: [{module, _screen_state} | _]} = state
) do ) do
if terminal_size_allowed(width, height) do if terminal_size_allowed(width, height) do
[ [

View File

@ -1,14 +1,30 @@
defmodule Chessh.SSH.Client.Board do defmodule Chessh.SSH.Client.Board do
use Chessh.SSH.Client.Screen alias Chessh.SSH.Client
alias Chessh.SSH.Client.State alias IO.ANSI
def render(%State{} = _state) do require Logger
@ascii_chars["pieces"]["white"]["knight"]
defmodule State do
defstruct cursor_x: 0,
cursor_y: 0
end end
def handle_input(action, state) do use Chessh.SSH.Client.Screen
def render(%Client.State{} = _state) do
knight = @ascii_chars["pieces"]["white"]["knight"]
[ANSI.home()] ++
Enum.map(
Enum.zip(0..(length(knight) - 1), knight),
fn {i, line} ->
[ANSI.cursor(i, 0), line]
end
)
end
def handle_input(action, %Client.State{} = state) do
case action do case action do
"q" -> state
_ -> state _ -> state
end end
end end

View File

@ -7,13 +7,13 @@ defmodule Chessh.SSH.Client.Menu do
defmodule State do defmodule State do
defstruct y: 0, defstruct y: 0,
x: 0 x: 0,
selected: 0
end end
use Chessh.SSH.Client.Screen use Chessh.SSH.Client.Screen
@logo " Simponic's @logo " Simponic's
dP MP\"\"\"\"\"\"`MM MP\"\"\"\"\"\"`MM M\"\"MMMMM\"\"MM dP MP\"\"\"\"\"\"`MM MP\"\"\"\"\"\"`MM M\"\"MMMMM\"\"MM
88 M mmmmm..M M mmmmm..M M MMMMM MM 88 M mmmmm..M M mmmmm..M M MMMMM MM
.d8888b. 88d888b. .d8888b. M. `YM M. `YM M `M .d8888b. 88d888b. .d8888b. M. `YM M. `YM M `M
@ -22,54 +22,80 @@ defmodule Chessh.SSH.Client.Menu do
`88888P' dP dP `88888P' Mb. .dM Mb. .dM M MMMMM MM `88888P' dP dP `88888P' Mb. .dM Mb. .dM M MMMMM MM
MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM" MMMMMMMMMMM MMMMMMMMMMM MMMMMMMMMMMM"
@options [
{"Option 1", {Chessh.SSH.Client.Board, %{}}},
{"Option 2", {Chessh.SSH.Client.Board, %{}}},
{"Option 3", {Chessh.SSH.Client.Board, %{}}}
]
def render(%Client.State{ def render(%Client.State{
width: width, width: width,
height: height, height: height,
state_stack: [{_this_module, %State{y: y, x: x}} | _tail] state_stack: [{_this_module, %State{selected: selected, y: dy, x: dx}} | _tail]
}) do }) do
text = String.split(@logo, "\n")
{logo_width, logo_height} = Utils.text_dim(@logo) {logo_width, logo_height} = Utils.text_dim(@logo)
{y, x} = center_rect({logo_width, logo_height + length(text)}, {width, height})
split = String.split(@logo, "\n")
Enum.flat_map( Enum.flat_map(
Enum.zip(0..(length(split) - 1), split), Enum.zip(0..(length(text) - 1), text),
fn {i, line} -> fn {i, line} ->
[ [
ANSI.cursor(div(height - logo_height, 2) + i + y, div(width - logo_width, 2) + x), ANSI.cursor(y + i + dy, x + dx),
"#{line}\n" line
] ]
end end
) ) ++
Enum.flat_map(
Enum.zip(0..(length(@options) - 1), @options),
fn {i, {option, _}} ->
[
ANSI.cursor(y + length(text) + i + dy, x + dx),
if(i == selected, do: ANSI.format([:light_cyan, "* #{option}"]), else: option)
]
end
) ++ [ANSI.home()]
end
def wrap_around(index, delta, length) do
calc = index + delta
if(calc < 0, do: length, else: 0) + rem(calc, length)
end end
def handle_input( def handle_input(
data, data,
%Client.State{state_stack: [{this_module, %State{y: y, x: x} = screen_state} | tail]} = %Client.State{
state state_stack:
[{this_module, %State{selected: selected} = screen_state} | tail] = state_stack
} = state
) do ) do
case data do case(data) do
:left ->
%Client.State{
state
| state_stack: [{this_module, %State{screen_state | x: x - 1}} | tail]
}
:right ->
%Client.State{
state
| state_stack: [{this_module, %State{screen_state | x: x + 1}} | tail]
}
:up -> :up ->
%Client.State{ %Client.State{
state state
| state_stack: [{this_module, %State{screen_state | y: y - 1}} | tail] | state_stack: [
{this_module,
%State{screen_state | selected: wrap_around(selected, -1, length(@options))}}
| tail
]
} }
:down -> :down ->
%Client.State{ %Client.State{
state state
| state_stack: [{this_module, %State{screen_state | y: y + 1}} | tail] | state_stack: [
{this_module,
%State{screen_state | selected: wrap_around(selected, 1, length(@options))}}
| tail
]
}
:return ->
{_, new_state} = Enum.at(@options, selected)
%Client.State{
state
| state_stack: [new_state] ++ state_stack
} }
_ -> _ ->

View File

@ -10,6 +10,13 @@ defmodule Chessh.SSH.Client.Screen do
@ascii_chars Application.compile_env!(:chessh, :ascii_chars_json_file) @ascii_chars Application.compile_env!(:chessh, :ascii_chars_json_file)
|> File.read!() |> File.read!()
|> Jason.decode!() |> Jason.decode!()
def center_rect({rect_width, rect_height}, {parent_width, parent_height}) do
{
div(parent_height - rect_height, 2),
div(parent_width - rect_width, 2)
}
end
end end
end end
end end