Skip to content

Commit c106278

Browse files
committed
example: add ToDoDemo
More complex usage of lenses
1 parent 37433c8 commit c106278

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package jtamaro.example.interaction;
2+
3+
import jtamaro.data.Function2;
4+
import jtamaro.data.Sequence;
5+
import jtamaro.data.Sequences;
6+
import jtamaro.graphic.Actionable;
7+
import jtamaro.graphic.Graphic;
8+
import jtamaro.graphic.Graphics;
9+
import jtamaro.interaction.KeyboardChar;
10+
import jtamaro.interaction.KeyboardKey;
11+
import jtamaro.optics.Glasses;
12+
import jtamaro.optics.Lens;
13+
14+
import static jtamaro.data.Sequences.cons;
15+
import static jtamaro.data.Sequences.empty;
16+
import static jtamaro.graphic.Colors.BLACK;
17+
import static jtamaro.graphic.Colors.GREEN;
18+
import static jtamaro.graphic.Colors.RED;
19+
import static jtamaro.graphic.Colors.TRANSPARENT;
20+
import static jtamaro.graphic.Colors.WHITE;
21+
import static jtamaro.graphic.Fonts.SANS_SERIF;
22+
import static jtamaro.graphic.Graphics.beside;
23+
import static jtamaro.graphic.Graphics.compose;
24+
import static jtamaro.graphic.Graphics.pin;
25+
import static jtamaro.graphic.Graphics.rectangle;
26+
import static jtamaro.graphic.Graphics.text;
27+
import static jtamaro.graphic.Points.CENTER_LEFT;
28+
import static jtamaro.io.IO.interact;
29+
30+
public final class ToDoDemo {
31+
32+
@Glasses
33+
record Entry(String content, boolean isChecked) {
34+
35+
}
36+
37+
@Glasses
38+
record Model(
39+
String currentInput,
40+
Sequence<Entry> entries,
41+
Function2<Model, String, Model> onEnter
42+
) {
43+
44+
}
45+
46+
private static Graphic renderInputField(Model model) {
47+
return compose(
48+
pin(CENTER_LEFT,
49+
text(model.currentInput, SANS_SERIF, 20, WHITE)),
50+
pin(CENTER_LEFT,
51+
rectangle(500, 50, BLACK))
52+
);
53+
}
54+
55+
private static Graphic renderEntry(Lens<Model, Model, Entry, Entry> lens, Model model) {
56+
final Entry entry = lens.view(model);
57+
final Graphic textGraphic = text(entry.content, SANS_SERIF, 20, BLACK);
58+
final Graphic checkedGraphic = rectangle(
59+
20,
60+
20,
61+
entry.isChecked ? GREEN : RED
62+
);
63+
64+
final Graphic actionableCheckbox = new Actionable<Model>(checkedGraphic)
65+
.withMouseReleaseHandler((m, $$, $$$) ->
66+
lens.then(ToDoDemo$EntryLenses.isChecked).over(b -> !b, m))
67+
.asGraphic();
68+
69+
final Graphic entryGraphic = compose(
70+
pin(CENTER_LEFT,
71+
beside(actionableCheckbox,
72+
beside(rectangle(10, 0, TRANSPARENT),
73+
textGraphic))),
74+
pin(CENTER_LEFT,
75+
rectangle(500, 50, WHITE))
76+
);
77+
78+
return new Actionable<Model>(entryGraphic).withMouseReleaseHandler(
79+
(m, $$, $$$) -> new Model(
80+
entry.content,
81+
m.entries,
82+
(mo, newContent) -> ToDoDemo$ModelLenses.currentInput.set(
83+
"",
84+
ToDoDemo$ModelLenses.onEnter.set(
85+
ToDoDemo::addNewEntry,
86+
lens.then(ToDoDemo$EntryLenses.content).set(newContent, mo)
87+
)
88+
)
89+
)
90+
).asGraphic();
91+
}
92+
93+
private static Graphic render(Model model) {
94+
return Sequences.traverseEvery(ToDoDemo$ModelLenses.entries).foldMap(
95+
renderInputField(model),
96+
Graphics::above,
97+
lens -> renderEntry(lens, model),
98+
model
99+
);
100+
}
101+
102+
private static Model onKeyType(Model model, KeyboardChar keyboardChar) {
103+
final char c = keyboardChar.keyChar();
104+
if (Character.isLetterOrDigit(c) || Character.isSpaceChar(c)) {
105+
return ToDoDemo$ModelLenses.currentInput.over(
106+
input -> input + c,
107+
model
108+
);
109+
} else {
110+
return model;
111+
}
112+
}
113+
114+
private static Model onKeyPress(Model model, KeyboardKey key) {
115+
return switch (key.keyCode()) {
116+
// Remove last char
117+
case KeyboardKey.BACK_SPACE -> ToDoDemo$ModelLenses.currentInput.over(
118+
input -> switch (input.length()) {
119+
case 0, 1 -> "";
120+
default -> input.substring(0, input.length() - 1);
121+
},
122+
model
123+
);
124+
// onEnterAction
125+
case KeyboardKey.ENTER -> model.onEnter.apply(model, model.currentInput);
126+
default -> model;
127+
};
128+
}
129+
130+
private static Model addNewEntry(Model current, String content) {
131+
return new Model(
132+
"",
133+
cons(new Entry(current.currentInput, false), current.entries),
134+
ToDoDemo::addNewEntry
135+
);
136+
}
137+
138+
public static void main(String[] args) {
139+
interact(new Model("Hello!", empty(), ToDoDemo::addNewEntry))
140+
.withCanvasSize(500, 400)
141+
.withRenderer(ToDoDemo::render)
142+
.withKeyTypeHandler(ToDoDemo::onKeyType)
143+
.withKeyPressHandler(ToDoDemo::onKeyPress)
144+
.run();
145+
}
146+
}

0 commit comments

Comments
 (0)