Skip to content
This repository was archived by the owner on Dec 23, 2021. It is now read-only.

Commit 66f72f3

Browse files
Buttons Integration for clue (#286)
Co-authored-by: unknown <[email protected]>
1 parent 25eb3f6 commit 66f72f3

File tree

15 files changed

+1505
-162
lines changed

15 files changed

+1505
-162
lines changed

src/base_circuitpython/base_cp_constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@
66

77
IMG_DIR_NAME = "img"
88
SCREEN_HEIGHT_WIDTH = 240
9+
10+
EXPECTED_INPUT_BUTTONS = ["button_a", "button_b"]

src/base_circuitpython/displayio/group.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,6 @@ def __len__(self):
9494
return 0
9595
else:
9696
return len(self.__contents)
97+
98+
def pop(self, i=-1):
99+
return self.__contents.pop(i)

src/clue/adafruit_clue.py

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,65 @@ class Clue: # pylint: disable=too-many-instance-attributes, too-many-public-met
199199
RAINBOW = (RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE)
200200

201201
def __init__(self):
202+
self._a = False
203+
self._b = False
204+
self.__pressed_buttons = set()
202205
self._pixel = neopixel.NeoPixel(
203206
pin=CONSTANTS.CLUE_PIN, n=1, pixel_order=neopixel.RGB
204207
)
205208

209+
@property
210+
def button_a(self):
211+
"""``True`` when Button A is pressed. ``False`` if not.
212+
This example prints when button A is pressed.
213+
To use with the CLUE:
214+
.. code-block:: python
215+
from adafruit_clue import clue
216+
while True:
217+
if clue.button_a:
218+
print("Button A pressed")
219+
"""
220+
return self._a
221+
222+
@property
223+
def button_b(self):
224+
"""``True`` when Button B is pressed. ``False`` if not.
225+
This example prints when button B is pressed.
226+
To use with the CLUE:
227+
.. code-block:: python
228+
from adafruit_clue import clue
229+
while True:
230+
if clue.button_b:
231+
print("Button B pressed")
232+
"""
233+
return self._b
234+
235+
def __update_button(self, button, value):
236+
if button == "button_a":
237+
if value:
238+
self.__pressed_buttons.add("A")
239+
self._a = value
240+
elif button == "button_b":
241+
if value:
242+
self.__pressed_buttons.add("B")
243+
self._b = value
244+
245+
@property
246+
def were_pressed(self):
247+
"""Returns a set of the buttons that have been pressed.
248+
To use with the CLUE:
249+
.. code-block:: python
250+
from adafruit_clue import clue
251+
while True:
252+
print(clue.were_pressed)
253+
"""
254+
ret = self.__pressed_buttons.copy()
255+
self.__pressed_buttons.clear()
256+
return ret
257+
206258
@property
207259
def pixel(self):
208260
"""The NeoPixel RGB LED.
209-
.. image :: ../docs/_static/neopixel.jpg
210-
:alt: NeoPixel
211261
This example turns the NeoPixel purple.
212262
To use with the CLUE:
213263
.. code-block:: python
@@ -284,6 +334,15 @@ def simple_text_display(
284334
colors=colors,
285335
)
286336

337+
def update_state(self, new_state):
338+
self.__update_buttons(new_state)
339+
340+
# helpers
341+
def __update_buttons(self, new_state):
342+
# get button pushes
343+
for button_name in CONSTANTS.EXPECTED_INPUT_BUTTONS:
344+
self.__update_button(button_name, new_state.get(button_name))
345+
287346

288347
clue = Clue() # pylint: disable=invalid-name
289348
"""Object that is automatically created on import.

src/clue/test/test_adafruit_clue.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,20 @@ def test_clue_display_text(self):
5353
clue_data.show()
5454

5555
helper._Helper__test_image_equality(displayio.bmp_img, expected)
56+
57+
def test_buttons(self):
58+
BUTTON_A = "button_a"
59+
BUTTON_B = "button_b"
60+
61+
clue._Clue__update_button(BUTTON_A, True)
62+
assert clue.button_a
63+
clue._Clue__update_button(BUTTON_A, False)
64+
assert not clue.button_a
65+
66+
clue._Clue__update_button(BUTTON_B, True)
67+
assert clue.button_b
68+
clue._Clue__update_button(BUTTON_B, False)
69+
assert not clue.button_b
70+
71+
assert set(["A", "B"]) == clue.were_pressed
72+
assert set() == clue.were_pressed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as React from "react";
2+
import * as ReactDOM from "react-dom";
3+
import { IntlProvider } from "react-intl";
4+
import * as testRenderer from "react-test-renderer";
5+
import { Clue } from "./Clue";
6+
7+
describe("Clue component", () => {
8+
it("should render correctly", () => {
9+
const component = testRenderer
10+
.create(
11+
<IntlProvider locale="en">
12+
<Clue />
13+
</IntlProvider>
14+
)
15+
.toJSON();
16+
expect(component).toMatchSnapshot();
17+
});
18+
19+
it("should render without crashing", () => {
20+
const div = document.createElement("div");
21+
ReactDOM.render(
22+
<IntlProvider locale="en">
23+
<Clue />
24+
</IntlProvider>,
25+
div
26+
);
27+
ReactDOM.unmountComponentAtNode(div);
28+
});
29+
});

src/view/components/clue/ClueImage.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import * as React from "react";
55
import { VIEW_STATE } from "../../constants";
66
import CONSTANTS, { BUTTON_STYLING_CLASSES } from "../../constants";
77
import { ViewStateContext } from "../../context";
8-
import "../../styles/Microbit.css";
98
import { ClueSvg, IRefObject } from "./Clue_svg";
109

1110
interface EventTriggers {

src/view/components/clue/ClueSimulator.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
// DEVICE_LIST_KEY,
55
CONSTANTS,
66
DEFAULT_IMG_CLUE,
7+
DEVICE_LIST_KEY,
78
WEBVIEW_MESSAGES,
89
} from "../../constants";
910
import PlayLogo from "../../svgs/play_svg";
@@ -48,7 +49,9 @@ export class ClueSimulator extends React.Component<any, IState> {
4849
}
4950
handleMessage = (event: any): void => {
5051
const message = event.data;
51-
52+
if (message.active_device !== DEVICE_LIST_KEY.CLUE) {
53+
return;
54+
}
5255
switch (message.command) {
5356
case "reset-state":
5457
this.setState({

src/view/components/clue/Clue_svg.tsx

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// Licensed under the MIT license.
33

44
import * as React from "react";
5-
import CONSTANTS from "../../constants";
6-
import "../../styles/Clue.css";
5+
import "../../styles/SimulatorSvg.css";
76
import { DEFAULT_CLUE_STATE } from "./ClueSimulator";
7+
import CONSTANTS from "../../constants";
88
export interface IRefObject {
99
[key: string]: React.RefObject<SVGRectElement>;
1010
}
@@ -776,20 +776,76 @@ export class ClueSvg extends React.Component<IProps, {}> {
776776
transform="translate(-49.27 -48.48)"
777777
/>
778778
</g>
779-
<g id="Buttons">
779+
<g
780+
className="sim-button-group"
781+
focusable="true"
782+
tabIndex={0}
783+
role="button"
784+
aria-label="a"
785+
ref={this.buttonRefs.BTN_A}
786+
>
780787
<rect
781-
className="cls-10"
788+
className="sim-button-outer"
789+
x="13.19"
790+
y="100.56"
791+
width="35.04"
792+
height="34.82"
793+
rx="1.79"
794+
/>
795+
<circle
796+
className="sim-button"
797+
cx="30.71"
798+
cy="117.86"
799+
r="11.6"
800+
/>
801+
<circle
802+
className="cls-14"
803+
cx="17.49"
804+
cy="104.88"
805+
r="3.23"
806+
/>
807+
<circle
808+
className="cls-14"
809+
cx="43.92"
810+
cy="104.88"
811+
r="3.23"
812+
/>
813+
<circle
814+
className="cls-14"
815+
cx="43.92"
816+
cy="131.03"
817+
r="3.23"
818+
/>
819+
<circle
820+
className="cls-14"
821+
cx="17.49"
822+
cy="131.03"
823+
r="3.23"
824+
/>
825+
</g>
826+
<g
827+
className="sim-button-group"
828+
focusable="true"
829+
tabIndex={0}
830+
role="button"
831+
aria-label="b"
832+
ref={this.buttonRefs.BTN_B}
833+
>
834+
<rect
835+
className="sim-button-outer"
782836
x="258.06"
783837
y="100.27"
784838
width="35.04"
785839
height="34.82"
786840
rx="1.79"
787841
/>
788842
<circle
789-
className="cls-13"
843+
className="sim-button"
844+
focusable="false"
790845
cx="275.58"
791846
cy="117.58"
792847
r="11.6"
848+
style={{ strokeWidth: 0 }}
793849
/>
794850
<circle
795851
className="cls-14"
@@ -815,45 +871,59 @@ export class ClueSvg extends React.Component<IProps, {}> {
815871
cy="130.74"
816872
r="3.23"
817873
/>
874+
</g>
875+
<g
876+
className="sim-button-group"
877+
focusable="true"
878+
tabIndex={0}
879+
role="button"
880+
aria-label="ab"
881+
ref={this.buttonRefs.BTN_AB}
882+
>
818883
<rect
819-
className="cls-15"
820-
x="13.19"
821-
y="100.56"
884+
className="sim-button-outer"
885+
x="327.48"
886+
y="192.69"
822887
width="35.04"
823888
height="34.82"
824889
rx="1.79"
825890
/>
826891
<circle
827-
className="cls-13"
828-
cx="30.71"
829-
cy="117.86"
892+
className="sim-button"
893+
focusable="false"
894+
cx="345"
895+
cy="210"
830896
r="11.6"
897+
style={{ strokeWidth: 0 }}
831898
/>
832899
<circle
833900
className="cls-14"
834-
cx="17.49"
835-
cy="104.88"
901+
cx="331.79"
902+
cy="197.02"
836903
r="3.23"
837904
/>
838905
<circle
839906
className="cls-14"
840-
cx="43.92"
841-
cy="104.88"
907+
cx="358.21"
908+
cy="197.02"
842909
r="3.23"
843910
/>
844911
<circle
845912
className="cls-14"
846-
cx="43.92"
847-
cy="131.03"
913+
cx="358.21"
914+
cy="223.16"
848915
r="3.23"
849916
/>
850917
<circle
851918
className="cls-14"
852-
cx="17.49"
853-
cy="131.03"
919+
cx="331.79"
920+
cy="223.16"
854921
r="3.23"
855922
/>
856923
</g>
924+
<text x={330} y={180} className="sim-text-outside-clue">
925+
A+B
926+
</text>
857927
<g id="Buttons_at_top" data-name="Buttons at top">
858928
<rect
859929
className="cls-16"
@@ -961,7 +1031,7 @@ export class ClueSvg extends React.Component<IProps, {}> {
9611031
rx="18.28"
9621032
/>
9631033
</g>
964-
<text x={318} y={85} className="sim-text-outside">
1034+
<text x={318} y={85} className="sim-text-outside-clue">
9651035
Neopixel
9661036
</text>
9671037
<circle cx={345} cy={115} r="30" fill="url(#grad1)" />

0 commit comments

Comments
 (0)