diff --git a/src/base_circuitpython/base_cp_constants.py b/src/base_circuitpython/base_cp_constants.py index 53e627109..67cea36e0 100644 --- a/src/base_circuitpython/base_cp_constants.py +++ b/src/base_circuitpython/base_cp_constants.py @@ -6,3 +6,5 @@ IMG_DIR_NAME = "img" SCREEN_HEIGHT_WIDTH = 240 + +EXPECTED_INPUT_BUTTONS = ["button_a", "button_b"] diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index 2d3f3b8af..0eee8f96f 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -94,3 +94,6 @@ def __len__(self): return 0 else: return len(self.__contents) + + def pop(self, i=-1): + return self.__contents.pop(i) diff --git a/src/clue/adafruit_clue.py b/src/clue/adafruit_clue.py index bb4ef83b7..1a2f8cd72 100644 --- a/src/clue/adafruit_clue.py +++ b/src/clue/adafruit_clue.py @@ -199,15 +199,65 @@ class Clue: # pylint: disable=too-many-instance-attributes, too-many-public-met RAINBOW = (RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE) def __init__(self): + self._a = False + self._b = False + self.__pressed_buttons = set() self._pixel = neopixel.NeoPixel( pin=CONSTANTS.CLUE_PIN, n=1, pixel_order=neopixel.RGB ) + @property + def button_a(self): + """``True`` when Button A is pressed. ``False`` if not. + This example prints when button A is pressed. + To use with the CLUE: + .. code-block:: python + from adafruit_clue import clue + while True: + if clue.button_a: + print("Button A pressed") + """ + return self._a + + @property + def button_b(self): + """``True`` when Button B is pressed. ``False`` if not. + This example prints when button B is pressed. + To use with the CLUE: + .. code-block:: python + from adafruit_clue import clue + while True: + if clue.button_b: + print("Button B pressed") + """ + return self._b + + def __update_button(self, button, value): + if button == "button_a": + if value: + self.__pressed_buttons.add("A") + self._a = value + elif button == "button_b": + if value: + self.__pressed_buttons.add("B") + self._b = value + + @property + def were_pressed(self): + """Returns a set of the buttons that have been pressed. + To use with the CLUE: + .. code-block:: python + from adafruit_clue import clue + while True: + print(clue.were_pressed) + """ + ret = self.__pressed_buttons.copy() + self.__pressed_buttons.clear() + return ret + @property def pixel(self): """The NeoPixel RGB LED. - .. image :: ../docs/_static/neopixel.jpg - :alt: NeoPixel This example turns the NeoPixel purple. To use with the CLUE: .. code-block:: python @@ -284,6 +334,15 @@ def simple_text_display( colors=colors, ) + def update_state(self, new_state): + self.__update_buttons(new_state) + + # helpers + def __update_buttons(self, new_state): + # get button pushes + for button_name in CONSTANTS.EXPECTED_INPUT_BUTTONS: + self.__update_button(button_name, new_state.get(button_name)) + clue = Clue() # pylint: disable=invalid-name """Object that is automatically created on import. diff --git a/src/clue/test/test_adafruit_clue.py b/src/clue/test/test_adafruit_clue.py index 9f516eac7..f634e5ae3 100644 --- a/src/clue/test/test_adafruit_clue.py +++ b/src/clue/test/test_adafruit_clue.py @@ -53,3 +53,20 @@ def test_clue_display_text(self): clue_data.show() helper._Helper__test_image_equality(displayio.bmp_img, expected) + + def test_buttons(self): + BUTTON_A = "button_a" + BUTTON_B = "button_b" + + clue._Clue__update_button(BUTTON_A, True) + assert clue.button_a + clue._Clue__update_button(BUTTON_A, False) + assert not clue.button_a + + clue._Clue__update_button(BUTTON_B, True) + assert clue.button_b + clue._Clue__update_button(BUTTON_B, False) + assert not clue.button_b + + assert set(["A", "B"]) == clue.were_pressed + assert set() == clue.were_pressed diff --git a/src/view/components/clue/Clue.spec.tsx b/src/view/components/clue/Clue.spec.tsx new file mode 100644 index 000000000..c69b4ebe7 --- /dev/null +++ b/src/view/components/clue/Clue.spec.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import { IntlProvider } from "react-intl"; +import * as testRenderer from "react-test-renderer"; +import { Clue } from "./Clue"; + +describe("Clue component", () => { + it("should render correctly", () => { + const component = testRenderer + .create( + + + + ) + .toJSON(); + expect(component).toMatchSnapshot(); + }); + + it("should render without crashing", () => { + const div = document.createElement("div"); + ReactDOM.render( + + + , + div + ); + ReactDOM.unmountComponentAtNode(div); + }); +}); diff --git a/src/view/components/clue/ClueImage.tsx b/src/view/components/clue/ClueImage.tsx index 6d74a2781..402572c95 100644 --- a/src/view/components/clue/ClueImage.tsx +++ b/src/view/components/clue/ClueImage.tsx @@ -5,7 +5,6 @@ import * as React from "react"; import { VIEW_STATE } from "../../constants"; import CONSTANTS, { BUTTON_STYLING_CLASSES } from "../../constants"; import { ViewStateContext } from "../../context"; -import "../../styles/Microbit.css"; import { ClueSvg, IRefObject } from "./Clue_svg"; interface EventTriggers { diff --git a/src/view/components/clue/ClueSimulator.tsx b/src/view/components/clue/ClueSimulator.tsx index 3a9e938df..3fd03cfc2 100644 --- a/src/view/components/clue/ClueSimulator.tsx +++ b/src/view/components/clue/ClueSimulator.tsx @@ -4,6 +4,7 @@ import { // DEVICE_LIST_KEY, CONSTANTS, DEFAULT_IMG_CLUE, + DEVICE_LIST_KEY, WEBVIEW_MESSAGES, } from "../../constants"; import PlayLogo from "../../svgs/play_svg"; @@ -48,7 +49,9 @@ export class ClueSimulator extends React.Component { } handleMessage = (event: any): void => { const message = event.data; - + if (message.active_device !== DEVICE_LIST_KEY.CLUE) { + return; + } switch (message.command) { case "reset-state": this.setState({ diff --git a/src/view/components/clue/Clue_svg.tsx b/src/view/components/clue/Clue_svg.tsx index e54c55c47..f5963309f 100644 --- a/src/view/components/clue/Clue_svg.tsx +++ b/src/view/components/clue/Clue_svg.tsx @@ -2,9 +2,9 @@ // Licensed under the MIT license. import * as React from "react"; -import CONSTANTS from "../../constants"; -import "../../styles/Clue.css"; +import "../../styles/SimulatorSvg.css"; import { DEFAULT_CLUE_STATE } from "./ClueSimulator"; +import CONSTANTS from "../../constants"; export interface IRefObject { [key: string]: React.RefObject; } @@ -776,9 +776,63 @@ export class ClueSvg extends React.Component { transform="translate(-49.27 -48.48)" /> - + + + + + + + + + { rx="1.79" /> { cy="130.74" r="3.23" /> + + + + A+B + { rx="18.28" /> - + Neopixel diff --git a/src/view/components/clue/__snapshots__/Clue.spec.tsx.snap b/src/view/components/clue/__snapshots__/Clue.spec.tsx.snap new file mode 100644 index 000000000..099a3de7a --- /dev/null +++ b/src/view/components/clue/__snapshots__/Clue.spec.tsx.snap @@ -0,0 +1,1160 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Clue component should render correctly 1`] = ` +Array [ +
+
+ The simulator will run the .py file you have focused on. +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A+B + + + + + + + + LIGHT + + + GESTURE + + + P + + R + + + O + + + XIMI + + + T + + + Y + + + + + + + + + + + + + + Neopixel + + + + +
+
+
+ + +
+
, +
+
+
+
+
, +] +`; diff --git a/src/view/components/cpx/Cpx.spec.tsx b/src/view/components/cpx/Cpx.spec.tsx index deb6da57d..24c53d551 100644 --- a/src/view/components/cpx/Cpx.spec.tsx +++ b/src/view/components/cpx/Cpx.spec.tsx @@ -4,7 +4,7 @@ import { IntlProvider } from "react-intl"; import * as testRenderer from "react-test-renderer"; import { Cpx } from "./Cpx"; -describe("Device component", () => { +describe("CPX component", () => { it("should render correctly", () => { const component = testRenderer .create( diff --git a/src/view/components/cpx/__snapshots__/Cpx.spec.tsx.snap b/src/view/components/cpx/__snapshots__/Cpx.spec.tsx.snap index 5120fb0a4..3a41b60f6 100644 --- a/src/view/components/cpx/__snapshots__/Cpx.spec.tsx.snap +++ b/src/view/components/cpx/__snapshots__/Cpx.spec.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Device component should render correctly 1`] = ` +exports[`CPX component should render correctly 1`] = ` Array [
; } diff --git a/src/view/styles/Clue.css b/src/view/styles/Clue.css deleted file mode 100644 index db64fe630..000000000 --- a/src/view/styles/Clue.css +++ /dev/null @@ -1,135 +0,0 @@ -.cls-1 { - fill: #097054; -} -.cls-1, -.cls-13, -.cls-14, -.cls-15, -.cls-23, -.cls-9 { - stroke: #000; -} -.cls-1, -.cls-11, -.cls-12, -.cls-13, -.cls-14, -.cls-15, -.cls-17, -.cls-22, -.cls-23, -.cls-3, -.cls-4, -.cls-5, -.cls-6, -.cls-9 { - stroke-miterlimit: 10; -} -.cls-1, -.cls-23 { - stroke-width: 1.99px; -} -.cls-18, -.cls-2 { - fill: #fff; -} -.cls-11, -.cls-12, -.cls-17, -.cls-23, -.cls-3, -.cls-4, -.cls-5, -.cls-6, -.cls-7, -.cls-8 { - fill: none; -} -.cls-11, -.cls-12, -.cls-17, -.cls-3, -.cls-4, -.cls-5, -.cls-6, -.cls-7, -.cls-8 { - stroke: #fff; -} -.cls-3 { - stroke-width: 2px; -} -.cls-12, -.cls-17, -.cls-4, -.cls-5, -.cls-6, -.cls-7, -.cls-8 { - stroke-linecap: round; -} -.cls-4, -.cls-7 { - stroke-width: 1.63px; -} -.cls-5 { - stroke-width: 1.41px; -} -.cls-6 { - stroke-width: 1.6px; -} -.cls-7, -.cls-8 { - stroke-linejoin: round; -} -.cls-8 { - stroke-width: 1.63px; -} -.cls-10, -.cls-15, -.cls-9 { - fill: #7e7272; -} -.cls-22, -.cls-9 { - stroke-width: 1.5px; -} -.cls-12 { - stroke-width: 1.71px; -} -.cls-13 { - fill: #6599ff; -} -.cls-13, -.cls-14, -.cls-15 { - stroke-width: 0.25px; -} -.cls-16 { - fill: #ffde00; -} -.cls-17 { - stroke-width: 2.02px; -} -.cls-18 { - font-size: 7px; - font-family: SegoeUI-Bold, Segoe UI; - font-weight: 700; - letter-spacing: -0.02em; -} -.cls-19 { - letter-spacing: -0.03em; -} -.cls-20 { - letter-spacing: -0.05em; -} -.cls-21 { - letter-spacing: 0em; -} -.cls-22 { - stroke: #7e7272; -} -.sim-text-outside { - font-size: 14px; - fill: var(--vscode-foreground); -} diff --git a/src/view/styles/Microbit.css b/src/view/styles/SimulatorSvg.css similarity index 62% rename from src/view/styles/Microbit.css rename to src/view/styles/SimulatorSvg.css index 44aed7e9a..9e9deb4fb 100644 --- a/src/view/styles/Microbit.css +++ b/src/view/styles/SimulatorSvg.css @@ -38,6 +38,9 @@ sim-button { .sim-button-outer:active { fill: orange; } +.sim-button-outer { + fill: #7e7272; +} .sim-button-key-press { fill: orange; @@ -174,3 +177,135 @@ sim-button { -webkit-user-select: none; -ms-user-select: none; } +.sim-button { + stroke: none; +} +.sim-button:active { + stroke: none; +} +.cls-1 { + fill: #097054; +} +.cls-1, +.cls-14, +.cls-23, +.cls-9 { + stroke: #000; +} +.cls-1, +.cls-11, +.cls-12, +.cls-14, +.cls-17, +.cls-22, +.cls-23, +.cls-3, +.cls-4, +.cls-5, +.cls-6, +.cls-9 { + stroke-miterlimit: 10; +} +.cls-1, +.cls-23 { + stroke-width: 1.99px; +} +.cls-18, +.cls-2 { + fill: #fff; +} +.cls-11, +.cls-12, +.cls-17, +.cls-23, +.cls-3, +.cls-4, +.cls-5, +.cls-6, +.cls-7, +.cls-8 { + fill: none; +} +.cls-11, +.cls-12, +.cls-17, +.cls-3, +.cls-4, +.cls-5, +.cls-6, +.cls-7, +.cls-8 { + stroke: #fff; +} +.cls-3 { + stroke-width: 2px; +} +.cls-12, +.cls-17, +.cls-4, +.cls-5, +.cls-6, +.cls-7, +.cls-8 { + stroke-linecap: round; +} +.cls-4, +.cls-7 { + stroke-width: 1.63px; +} +.cls-5 { + stroke-width: 1.41px; +} +.cls-6 { + stroke-width: 1.6px; +} +.cls-7, +.cls-8 { + stroke-linejoin: round; +} +.cls-8 { + stroke-width: 1.63px; +} +.cls-10, +.cls-9 { + fill: #7e7272; +} +.cls-22, +.cls-9 { + stroke-width: 1.5px; +} +.cls-12 { + stroke-width: 1.71px; +} + +.cls-14 { + stroke-width: 0.25px; +} +.cls-16 { + fill: #ffde00; +} +.cls-17 { + stroke-width: 2.02px; +} +.cls-18 { + font-size: 7px; + font-family: SegoeUI-Bold, Segoe UI; + font-weight: 700; + letter-spacing: -0.02em; +} +.cls-19 { + letter-spacing: -0.03em; +} +.cls-20 { + letter-spacing: -0.05em; +} +.cls-21 { + letter-spacing: 0em; +} +.cls-22 { + stroke: #7e7272; +} +.sim-text-outside-clue { + font-size: 14px; + fill: var(--vscode-foreground); +}