-
Notifications
You must be signed in to change notification settings - Fork 55
/
ratatouille.exs
128 lines (100 loc) · 2.72 KB
/
ratatouille.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
Mix.install([:ratatouille])
defmodule Main do
def main do
# there isn't a way to return output from the tui, so we use some global state
Agent.start_link(fn -> nil end, name: :capture_proc)
Ratatouille.run(RatatouilleSelector,
quit_events: [
{:key, Ratatouille.Constants.key(:ctrl_c)},
{:key, Ratatouille.Constants.key(:esc)}
]
)
selection = Agent.get(:capture_proc, & &1)
IO.puts(selection)
end
end
defmodule RatatouilleSelector do
@behaviour Ratatouille.App
import Ratatouille.View
import Ratatouille.Constants
@up [key(:ctrl_p), key(:arrow_up)]
@down [key(:ctrl_n), key(:arrow_down)]
@enter key(:enter)
def init(%{window: %{height: height}}) do
lines = ~w(foo bar baz)
%{
lines: lines,
selected_row: 0,
height: height - 1
}
end
def update(%{lines: lines} = model, msg) do
model
|> then(&on_arrow_pressed(msg, lines, &1))
|> then(&on_enter_pressed(msg, &1))
end
def render(%{lines: lines, selected_row: selected_row}) do
view do
viewport do
for {line, idx} <- Enum.with_index(lines) do
row do
column size: 12 do
label do
arrow(idx == selected_row)
gutter(idx == selected_row)
line(line, %{selected?: idx == selected_row})
end
end
end
end
end
end
end
defp line(l, %{selected?: true}) do
text(content: l, color: :black, background: :white)
end
defp line(l, %{selected?: false}) do
text(content: l, color: :default, background: :default)
end
defp gutter(true) do
text(content: " ", background: :white)
end
defp gutter(false) do
text(content: " ", background: :default)
end
defp arrow(true) do
text(content: ">", color: :red, background: :white)
end
defp arrow(false) do
text(content: " ", background: :white)
end
def on_arrow_pressed(msg, lines, model) do
case msg do
{:event, %{key: key}} when key in @up ->
%{model | selected_row: max(model.selected_row - 1, 0)}
{:event, %{key: key}} when key in @down ->
%{model | selected_row: min(model.selected_row + 1, length(lines) - 1)}
_ ->
model
end
end
def on_enter_pressed(msg, model) do
case msg do
{:event, %{key: @enter}} ->
word =
model.lines
|> Enum.with_index(fn f, i -> {i, f} end)
|> then(&Map.new(&1)[model.selected_row])
Agent.update(:capture_proc, fn _ -> word end)
quit()
model
_ ->
model
end
end
defp quit() do
# we fake a keystroke to quit the tui
send(self(), {:event, %{key: key(:esc)}})
end
end
Main.main()