diff --git a/src/adafruit_circuitplayground/express.py b/src/adafruit_circuitplayground/express.py index 59e501e8c..40b82adff 100644 --- a/src/adafruit_circuitplayground/express.py +++ b/src/adafruit_circuitplayground/express.py @@ -46,8 +46,7 @@ def __init__(self): "touch": [False] * 7, "shake": False, } - self.__debug_mode = False - self.pixels = Pixel(self.__state, self.__debug_mode) + self.pixels = Pixel(self.__state) @property def acceleration(self): @@ -113,7 +112,7 @@ def light(self): return self.__state["light"] def __show(self): - if self.__debug_mode: + if utils.debug_mode: common.debugger_communication_client.debug_send_to_simulator( self.__state, CONSTANTS.CPX ) diff --git a/src/adafruit_circuitplayground/pixel.py b/src/adafruit_circuitplayground/pixel.py index 18e0028fc..758084f59 100644 --- a/src/adafruit_circuitplayground/pixel.py +++ b/src/adafruit_circuitplayground/pixel.py @@ -12,16 +12,15 @@ class Pixel: - def __init__(self, state, debug_mode=False): + def __init__(self, state): self.__state = state self.auto_write = True - self.__debug_mode = debug_mode self.telemetry_state = False def show(self): # Send the state to the extension so that React re-renders the Webview # or send the state to the debugger (within this library) - if self.__debug_mode: + if utils.debug_mode: common.debugger_communication_client.debug_send_to_simulator( self.__state, CONSTANTS.CPX ) @@ -32,9 +31,6 @@ def __show_if_auto_write(self): if self.auto_write: self.show() - def __set_debug_mode(self, debug_mode): - self.__debug_mode = debug_mode - def __getitem__(self, index): if type(index) is not slice: if not self.__valid_index(index): diff --git a/src/adafruit_circuitplayground/test/test_pixel.py b/src/adafruit_circuitplayground/test/test_pixel.py index 2532611db..521958552 100644 --- a/src/adafruit_circuitplayground/test/test_pixel.py +++ b/src/adafruit_circuitplayground/test/test_pixel.py @@ -16,11 +16,6 @@ def setup_method(self): } ) - @pytest.mark.parametrize("debug_mode", [True, False]) - def test_set_debug_mode(self, debug_mode): - self.pixel._Pixel__set_debug_mode(debug_mode) - assert debug_mode == self.pixel._Pixel__debug_mode - def test_get_item_out_of_bounds(self): with pytest.raises(IndexError): p = self.pixel[3] diff --git a/src/base_circuitpython/displayio/group.py b/src/base_circuitpython/displayio/group.py index 1f66b291d..edae6f3e2 100644 --- a/src/base_circuitpython/displayio/group.py +++ b/src/base_circuitpython/displayio/group.py @@ -288,4 +288,22 @@ def __show(self, img): img_str = str(byte_base64)[2:-1] sendable_json = {CONSTANTS.BASE_64: img_str} - common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) + + if common.utils.debug_mode: + common.debugger_communication_client.debug_send_to_simulator( + sendable_json, CONSTANTS.CLUE + ) + else: + common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) + + def __len__(self): + if not self.__contents: + return 0 + else: + return len(self.__contents) + + def pop(self, i=-1): + item = self.__contents.pop(i) + item.parent = None + self.elem_changed() + return item diff --git a/src/base_circuitpython/neopixel_write.py b/src/base_circuitpython/neopixel_write.py index 8862863e8..341215f35 100644 --- a/src/base_circuitpython/neopixel_write.py +++ b/src/base_circuitpython/neopixel_write.py @@ -8,7 +8,7 @@ import sys import os -from common import utils +import common from adafruit_circuitplayground import cp import base_cp_constants as CONSTANTS @@ -30,7 +30,12 @@ def neopixel_write(gpio, buf): def send_clue(buf): sendable_json = {CONSTANTS.PIXELS: tuple(buf)} - utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) + if common.utils.debug_mode: + common.debugger_communication_client.debug_send_to_simulator( + sendable_json, CONSTANTS.CLUE + ) + else: + common.utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) def send_cpx(buf): diff --git a/src/clue/adafruit_slideshow.py b/src/clue/adafruit_slideshow.py index daa522d52..f99a2647b 100644 --- a/src/clue/adafruit_slideshow.py +++ b/src/clue/adafruit_slideshow.py @@ -8,6 +8,7 @@ import collections from random import shuffle from common import utils +from common import debugger_communication_client from common.telemetry import telemetry_py from common.telemetry_events import TelemetryEvent import board @@ -380,4 +381,10 @@ def __send(self, img): img_str = str(byte_base64)[2:-1] sendable_json = {CONSTANTS.BASE_64: img_str} - utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) + + if utils.debug_mode: + debugger_communication_client.debug_send_to_simulator( + sendable_json, CONSTANTS.CLUE + ) + else: + utils.send_to_simulator(sendable_json, CONSTANTS.CLUE) diff --git a/src/common/debugger_communication_client.py b/src/common/debugger_communication_client.py index 23035c6f9..11273f053 100644 --- a/src/common/debugger_communication_client.py +++ b/src/common/debugger_communication_client.py @@ -5,20 +5,34 @@ import json import socketio import copy +import pathlib from . import constants as CONSTANTS from . import utils import threading - +import os +import python_constants as TOPLEVEL_CONSTANTS from adafruit_circuitplayground.express import cpx from adafruit_circuitplayground.constants import CPX +# add ref for micropython and clue +abs_path_to_parent_dir = os.path.dirname( + os.path.join(pathlib.Path(__file__).parent, "..", "..") +) +sys.path.insert( + 0, os.path.join(abs_path_to_parent_dir, TOPLEVEL_CONSTANTS.MICROPYTHON_LIBRARY_NAME) +) + +sys.path.insert(0, os.path.join(abs_path_to_parent_dir, TOPLEVEL_CONSTANTS.CLUE_DIR)) + from microbit.__model.microbit_model import __mb as mb from microbit.__model.constants import MICROBIT +from base_circuitpython.base_cp_constants import CLUE +from adafruit_clue import clue -device_dict = {CPX: cpx, MICROBIT: mb} +device_dict = {CPX: cpx, MICROBIT: mb, CLUE: clue} processing_state_event = threading.Event() previous_state = {} diff --git a/src/common/utils.py b/src/common/utils.py index f78dda44d..1af1c0eb5 100644 --- a/src/common/utils.py +++ b/src/common/utils.py @@ -10,6 +10,7 @@ previous_state = {} abs_path_to_user_file = "" +debug_mode = False def update_state_with_device_name(state, device_name): diff --git a/src/debug_user_code.py b/src/debug_user_code.py index 93b6ba50f..7e534f479 100644 --- a/src/debug_user_code.py +++ b/src/debug_user_code.py @@ -26,15 +26,17 @@ ) sys.path.insert(0, abs_path_to_micropython_lib) +# Insert absolute path to library for CLUE into sys.path +sys.path.insert(0, os.path.join(abs_path_to_parent_dir, CONSTANTS.CLUE)) + +# Insert absolute path to Circuitpython libraries for CLUE into sys.path +sys.path.insert(0, os.path.join(abs_path_to_parent_dir, CONSTANTS.CIRCUITPYTHON)) + # This import must happen after the sys.path is modified -from adafruit_circuitplayground.express import cpx -from microbit.__model.microbit_model import __mb as mb from common import debugger_communication_client - ## Execute User Code ## - # Get user's code path abs_path_to_code_file = "" if len(sys.argv) > 1 and sys.argv[1]: @@ -52,9 +54,7 @@ # Init API variables utils.abs_path_to_user_file = abs_path_to_code_file -cpx._Express__debug_mode = True -cpx.pixels._Pixel__set_debug_mode(True) -mb._MicrobitModel__set_debug_mode(True) +utils.debug_mode = True # Execute the user's code file with open(abs_path_to_code_file, encoding="utf8") as user_code_file: diff --git a/src/micropython/microbit/__model/display.py b/src/micropython/microbit/__model/display.py index 1075f2c56..523811db0 100644 --- a/src/micropython/microbit/__model/display.py +++ b/src/micropython/microbit/__model/display.py @@ -21,7 +21,6 @@ def __init__(self): self.__current_pid = None self.__lock = threading.Lock() - self.__debug_mode = False def scroll(self, value, delay=150, wait=True, loop=False, monospace=False): """ @@ -352,7 +351,7 @@ def __create_scroll_image(images): def __update_client(self): sendable_json = {"leds": self.__get_array()} - if self.__debug_mode: + if common.utils.debug_mode: common.debugger_communication_client.debug_send_to_simulator( sendable_json, CONSTANTS.MICROBIT ) diff --git a/src/micropython/microbit/__model/microbit_model.py b/src/micropython/microbit/__model/microbit_model.py index bed74d9cd..b9149e721 100644 --- a/src/micropython/microbit/__model/microbit_model.py +++ b/src/micropython/microbit/__model/microbit_model.py @@ -98,8 +98,5 @@ def __update_gesture(self, new_state): new_gesture = new_state.get(CONSTANTS.EXPECTED_INPUT_GESTURE) self.accelerometer._Accelerometer__update_gesture(new_gesture) - def __set_debug_mode(self, mode): - self.display._Display__debug_mode = mode - __mb = MicrobitModel() diff --git a/src/view/components/clue/ClueImage.tsx b/src/view/components/clue/ClueImage.tsx index 402572c95..7def6aa89 100644 --- a/src/view/components/clue/ClueImage.tsx +++ b/src/view/components/clue/ClueImage.tsx @@ -2,7 +2,7 @@ // Licensed under the MIT license. import * as React from "react"; -import { VIEW_STATE } from "../../constants"; +import { BUTTON_CLASSNAME, VIEW_STATE } from "../../constants"; import CONSTANTS, { BUTTON_STYLING_CLASSES } from "../../constants"; import { ViewStateContext } from "../../context"; import { ClueSvg, IRefObject } from "./Clue_svg"; @@ -19,11 +19,6 @@ interface IProps { neopixel: number[]; } -const BUTTON_CLASSNAME = { - ACTIVE: "sim-button-outer", - DEACTIVATED: "sim-button-deactivated", -}; - export enum BUTTONS_KEYS { BTN_A = "BTN_A", BTN_B = "BTN_B", @@ -118,6 +113,7 @@ const setupButton = ( eventTriggers: EventTriggers, key: string ) => { + buttonElement.setAttribute("class", BUTTON_CLASSNAME.ACTIVE); buttonElement.onmousedown = e => { buttonElement.focus(); eventTriggers.onMouseDown(e, key); diff --git a/src/view/components/clue/ClueSimulator.tsx b/src/view/components/clue/ClueSimulator.tsx index f48112a0a..61542d3a4 100644 --- a/src/view/components/clue/ClueSimulator.tsx +++ b/src/view/components/clue/ClueSimulator.tsx @@ -1,275 +1,284 @@ -import * as React from "react"; -import { - AB_BUTTONS_KEYS, - // DEVICE_LIST_KEY, - CONSTANTS, - DEFAULT_IMG_CLUE, - DEVICE_LIST_KEY, - WEBVIEW_MESSAGES, -} from "../../constants"; -import PlayLogo from "../../svgs/play_svg"; -import StopLogo from "../../svgs/stop_svg"; -import { sendMessage } from "../../utils/MessageUtils"; -import ActionBar from "../simulator/ActionBar"; -import { BUTTONS_KEYS, ClueImage } from "./ClueImage"; -import "../../styles/Simulator.css"; - -export const DEFAULT_CLUE_STATE: IClueState = { - buttons: { button_a: false, button_b: false }, - displayMessage: DEFAULT_IMG_CLUE, - neopixel: [0, 0, 0], -}; - -interface IState { - active_editors: string[]; - running_file?: string; - play_button: boolean; - selected_file: string; - clue: IClueState; - currently_selected_file: string; -} - -interface IClueState { - buttons: { button_a: boolean; button_b: boolean }; - displayMessage: string; - neopixel: number[]; -} -export class ClueSimulator extends React.Component { - private imageRef: React.RefObject = React.createRef(); - constructor() { - super({}); - this.state = { - clue: DEFAULT_CLUE_STATE, - play_button: false, - selected_file: "", - active_editors: [], - running_file: undefined, - currently_selected_file: "", - }; - this.onKeyEvent = this.onKeyEvent.bind(this); - } - 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({ - clue: DEFAULT_CLUE_STATE, - play_button: false, - }); - break; - case "set-state": - console.log( - `message received ${JSON.stringify(message.state)}` - ); - if (message.state.display_base64) { - this.setState({ - clue: { - ...this.state.clue, - displayMessage: message.state.display_base64, - }, - }); - } else if (message.state.pixels) { - this.setState({ - clue: { - ...this.state.clue, - neopixel: message.state.pixels, - }, - }); - } - - break; - case "activate-play": - const newRunningFile = this.state.currently_selected_file; - this.setState({ - play_button: !this.state.play_button, - running_file: newRunningFile, - clue: { - ...this.state.clue, - displayMessage: DEFAULT_IMG_CLUE, - }, - }); - break; - case "visible-editors": - this.setState({ - active_editors: message.state.activePythonEditors, - }); - break; - case "current-file": - if (this.state.play_button) { - this.setState({ - currently_selected_file: message.state.running_file, - }); - } else { - this.setState({ - running_file: message.state.running_file, - currently_selected_file: message.state.running_file, - }); - } - - break; - } - }; - componentDidMount() { - window.addEventListener("message", this.handleMessage); - } - componentWillUnmount() { - window.removeEventListener("message", this.handleMessage); - } - - render() { - const playStopImage = this.state.play_button ? StopLogo : PlayLogo; - const playStopLabel = this.state.play_button ? "stop" : "play"; - return ( -
-
- {this.state.running_file && this.state.play_button - ? CONSTANTS.CURRENTLY_RUNNING(this.state.running_file) - : CONSTANTS.FILES_PLACEHOLDER} -
- -
- -
- -
- ); - } - protected togglePlayClick = () => { - const button = - window.document.getElementById(CONSTANTS.ID_NAME.PLAY_BUTTON) || - window.document.getElementById(CONSTANTS.ID_NAME.STOP_BUTTON); - if (button) { - button.focus(); - } - sendMessage(WEBVIEW_MESSAGES.TOGGLE_PLAY_STOP, { - selected_file: this.state.selected_file, - state: !this.state.play_button, - }); - }; - protected onSelectFile(event: React.FocusEvent) { - this.setState({ - selected_file: event.currentTarget.value, - }); - } - protected refreshSimulatorClick = () => { - const button = window.document.getElementById( - CONSTANTS.ID_NAME.REFRESH_BUTTON - ); - if (button) { - button.focus(); - } - sendMessage(WEBVIEW_MESSAGES.REFRESH_SIMULATOR, true); - }; - protected handleButtonClick = (key: string, isActive: boolean) => { - let newButtonState = this.state.clue.buttons; - switch (key) { - case AB_BUTTONS_KEYS.BTN_A: - newButtonState.button_a = isActive; - break; - case AB_BUTTONS_KEYS.BTN_B: - newButtonState.button_b = isActive; - break; - case AB_BUTTONS_KEYS.BTN_AB: - newButtonState = { - button_a: isActive, - button_b: isActive, - }; - break; - } - sendMessage(WEBVIEW_MESSAGES.BUTTON_PRESS, newButtonState); - this.setState({ - clue: { - ...this.state.clue, - buttons: newButtonState, - }, - }); - }; - protected onMouseUp = (event: Event, key: string) => { - event.preventDefault(); - this.handleButtonClick(key, false); - }; - protected onMouseDown = (event: Event, key: string) => { - event.preventDefault(); - this.handleButtonClick(key, true); - }; - protected onMouseLeave = (event: Event, key: string) => { - event.preventDefault(); - console.log(`To implement onMouseLeave ${key}`); - }; - protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) { - event.stopPropagation(); - if ([event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.ENTER)) { - this.handleButtonClick(key, active); - if (this.imageRef.current) { - if (key === BUTTONS_KEYS.BTN_A) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_A, - active - ); - } else if (key === BUTTONS_KEYS.BTN_B) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_B, - active - ); - } else if (key === BUTTONS_KEYS.BTN_AB) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_AB, - active - ); - } - } - } else if ( - [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) - ) { - this.handleButtonClick(BUTTONS_KEYS.BTN_A, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_A, - active - ); - } - } else if ( - [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) - ) { - this.handleButtonClick(BUTTONS_KEYS.BTN_B, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_B, - active - ); - } - } else if ( - [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) - ) { - this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_AB, - active - ); - } - } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_F) { - this.togglePlayClick(); - } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_R) { - this.refreshSimulatorClick(); - } - } -} +import * as React from "react"; +import { + AB_BUTTONS_KEYS, + // DEVICE_LIST_KEY, + CONSTANTS, + DEFAULT_IMG_CLUE, + DEVICE_LIST_KEY, + VIEW_STATE, + WEBVIEW_MESSAGES, +} from "../../constants"; +import { ViewStateContext } from "../../context"; +import "../../styles/Simulator.css"; +import PlayLogo from "../../svgs/play_svg"; +import StopLogo from "../../svgs/stop_svg"; +import { sendMessage } from "../../utils/MessageUtils"; +import ActionBar from "../simulator/ActionBar"; +import { BUTTONS_KEYS, ClueImage } from "./ClueImage"; + +export const DEFAULT_CLUE_STATE: IClueState = { + buttons: { button_a: false, button_b: false }, + displayMessage: DEFAULT_IMG_CLUE, + neopixel: [0, 0, 0], +}; + +interface IState { + active_editors: string[]; + running_file?: string; + play_button: boolean; + selected_file: string; + clue: IClueState; + currently_selected_file: string; +} + +interface IClueState { + buttons: { button_a: boolean; button_b: boolean }; + displayMessage: string; + neopixel: number[]; +} +export class ClueSimulator extends React.Component { + private imageRef: React.RefObject = React.createRef(); + constructor() { + super({}); + this.state = { + clue: DEFAULT_CLUE_STATE, + play_button: false, + selected_file: "", + active_editors: [], + running_file: undefined, + currently_selected_file: "", + }; + this.onKeyEvent = this.onKeyEvent.bind(this); + } + 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({ + clue: DEFAULT_CLUE_STATE, + play_button: false, + }); + break; + case "set-state": + console.log( + `message received ${JSON.stringify(message.state)}` + ); + if (message.state.display_base64) { + this.setState({ + clue: { + ...this.state.clue, + displayMessage: message.state.display_base64, + }, + }); + } else if (message.state.pixels) { + this.setState({ + clue: { + ...this.state.clue, + neopixel: message.state.pixels, + }, + }); + } + + break; + case "activate-play": + const newRunningFile = this.state.currently_selected_file; + this.setState({ + play_button: !this.state.play_button, + running_file: newRunningFile, + clue: { + ...this.state.clue, + displayMessage: DEFAULT_IMG_CLUE, + }, + }); + break; + case "visible-editors": + this.setState({ + active_editors: message.state.activePythonEditors, + }); + break; + case "current-file": + if (this.state.play_button) { + this.setState({ + currently_selected_file: message.state.running_file, + }); + } else { + this.setState({ + running_file: message.state.running_file, + currently_selected_file: message.state.running_file, + }); + } + + break; + } + }; + componentDidMount() { + window.addEventListener("message", this.handleMessage); + } + componentWillUnmount() { + window.removeEventListener("message", this.handleMessage); + } + + render() { + const playStopImage = this.state.play_button ? StopLogo : PlayLogo; + const playStopLabel = this.state.play_button ? "stop" : "play"; + return ( +
+
+ {this.state.running_file && this.state.play_button + ? CONSTANTS.CURRENTLY_RUNNING(this.state.running_file) + : CONSTANTS.FILES_PLACEHOLDER} +
+ +
+ +
+ +
+ ); + } + protected togglePlayClick = () => { + const button = + window.document.getElementById(CONSTANTS.ID_NAME.PLAY_BUTTON) || + window.document.getElementById(CONSTANTS.ID_NAME.STOP_BUTTON); + if (button) { + button.focus(); + } + sendMessage(WEBVIEW_MESSAGES.TOGGLE_PLAY_STOP, { + selected_file: this.state.selected_file, + state: !this.state.play_button, + }); + }; + protected onSelectFile(event: React.FocusEvent) { + this.setState({ + selected_file: event.currentTarget.value, + }); + } + protected refreshSimulatorClick = () => { + const button = window.document.getElementById( + CONSTANTS.ID_NAME.REFRESH_BUTTON + ); + if (button) { + button.focus(); + } + sendMessage(WEBVIEW_MESSAGES.REFRESH_SIMULATOR, true); + }; + protected handleButtonClick = (key: string, isActive: boolean) => { + let newButtonState = this.state.clue.buttons; + switch (key) { + case AB_BUTTONS_KEYS.BTN_A: + newButtonState.button_a = isActive; + break; + case AB_BUTTONS_KEYS.BTN_B: + newButtonState.button_b = isActive; + break; + case AB_BUTTONS_KEYS.BTN_AB: + newButtonState = { + button_a: isActive, + button_b: isActive, + }; + break; + } + sendMessage(WEBVIEW_MESSAGES.BUTTON_PRESS, newButtonState); + this.setState({ + clue: { + ...this.state.clue, + buttons: newButtonState, + }, + }); + }; + protected onMouseUp = (event: Event, key: string) => { + event.preventDefault(); + this.handleButtonClick(key, false); + }; + protected onMouseDown = (event: Event, key: string) => { + event.preventDefault(); + this.handleButtonClick(key, true); + }; + protected onMouseLeave = (event: Event, key: string) => { + event.preventDefault(); + console.log(`To implement onMouseLeave ${key}`); + }; + protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) { + event.stopPropagation(); + if ( + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.ENTER) && + this.context === VIEW_STATE.RUNNING + ) { + this.handleButtonClick(key, active); + if (this.imageRef.current) { + if (key === BUTTONS_KEYS.BTN_A) { + this.imageRef.current.updateButtonAttributes( + BUTTONS_KEYS.BTN_A, + active + ); + } else if (key === BUTTONS_KEYS.BTN_B) { + this.imageRef.current.updateButtonAttributes( + BUTTONS_KEYS.BTN_B, + active + ); + } else if (key === BUTTONS_KEYS.BTN_AB) { + this.imageRef.current.updateButtonAttributes( + BUTTONS_KEYS.BTN_AB, + active + ); + } + } + } else if ( + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) && + this.context === VIEW_STATE.RUNNING + ) { + this.handleButtonClick(BUTTONS_KEYS.BTN_A, active); + if (this.imageRef.current) { + this.imageRef.current.updateButtonAttributes( + BUTTONS_KEYS.BTN_A, + active + ); + } + } else if ( + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) && + this.context === VIEW_STATE.RUNNING + ) { + this.handleButtonClick(BUTTONS_KEYS.BTN_B, active); + if (this.imageRef.current) { + this.imageRef.current.updateButtonAttributes( + BUTTONS_KEYS.BTN_B, + active + ); + } + } else if ( + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) && + this.context === VIEW_STATE.RUNNING + ) { + this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active); + if (this.imageRef.current) { + this.imageRef.current.updateButtonAttributes( + BUTTONS_KEYS.BTN_AB, + active + ); + } + } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_F) { + this.togglePlayClick(); + } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_R) { + this.refreshSimulatorClick(); + } + } +} +ClueSimulator.contextType = ViewStateContext; diff --git a/src/view/components/microbit/MicrobitImage.tsx b/src/view/components/microbit/MicrobitImage.tsx index 1b1cb9af3..c471ecb56 100644 --- a/src/view/components/microbit/MicrobitImage.tsx +++ b/src/view/components/microbit/MicrobitImage.tsx @@ -2,7 +2,7 @@ // Licensed under the MIT license. import * as React from "react"; -import { VIEW_STATE } from "../../constants"; +import { BUTTON_CLASSNAME, VIEW_STATE } from "../../constants"; import CONSTANTS, { BUTTON_STYLING_CLASSES } from "../../constants"; import { ViewStateContext } from "../../context"; import { IRefObject, MicrobitSvg } from "./Microbit_svg"; @@ -18,11 +18,6 @@ interface IProps { leds: number[][]; } -const BUTTON_CLASSNAME = { - ACTIVE: "sim-button-outer", - DEACTIVATED: "sim-button-deactivated", -}; - export enum BUTTONS_KEYS { BTN_A = "BTN_A", BTN_B = "BTN_B", @@ -113,6 +108,8 @@ const setupButton = ( eventTriggers: EventTriggers, key: string ) => { + buttonElement.setAttribute("class", BUTTON_CLASSNAME.ACTIVE); + buttonElement.onmousedown = e => { buttonElement.focus(); eventTriggers.onMouseDown(e, key); diff --git a/src/view/components/microbit/MicrobitSimulator.tsx b/src/view/components/microbit/MicrobitSimulator.tsx index 0a658298c..ff44ac7b2 100644 --- a/src/view/components/microbit/MicrobitSimulator.tsx +++ b/src/view/components/microbit/MicrobitSimulator.tsx @@ -3,8 +3,10 @@ import { AB_BUTTONS_KEYS, CONSTANTS, DEVICE_LIST_KEY, + VIEW_STATE, WEBVIEW_MESSAGES, } from "../../constants"; +import { ViewStateContext } from "../../context"; import PlayLogo from "../../svgs/play_svg"; import StopLogo from "../../svgs/stop_svg"; import { sendMessage } from "../../utils/MessageUtils"; @@ -198,7 +200,10 @@ export class MicrobitSimulator extends React.Component { }; protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) { event.stopPropagation(); - if ([event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.ENTER)) { + if ( + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.ENTER) && + this.context === VIEW_STATE.RUNNING + ) { this.handleButtonClick(key, active); if (this.imageRef.current) { if (key === BUTTONS_KEYS.BTN_A) { @@ -219,7 +224,8 @@ export class MicrobitSimulator extends React.Component { } } } else if ( - [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) && + this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_A, active); if (this.imageRef.current) { @@ -229,7 +235,8 @@ export class MicrobitSimulator extends React.Component { ); } } else if ( - [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) && + this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_B, active); if (this.imageRef.current) { @@ -239,7 +246,8 @@ export class MicrobitSimulator extends React.Component { ); } } else if ( - [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) + [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) && + this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active); if (this.imageRef.current) { @@ -255,3 +263,4 @@ export class MicrobitSimulator extends React.Component { } } } +MicrobitSimulator.contextType = ViewStateContext; diff --git a/src/view/components/toolbar/SensorButton.tsx b/src/view/components/toolbar/SensorButton.tsx index 75f93b2fb..601bb80c1 100644 --- a/src/view/components/toolbar/SensorButton.tsx +++ b/src/view/components/toolbar/SensorButton.tsx @@ -2,6 +2,8 @@ // Licensed under the MIT license. import * as React from "react"; +import { VIEW_STATE } from "../../constants"; +import { ViewStateContext } from "../../context"; import "../../styles/SensorButton.css"; import { ISensorButtonProps } from "../../viewUtils"; @@ -9,20 +11,25 @@ class SensorButton extends React.Component { private buttonRef: React.RefObject = React.createRef(); public setButtonClass = (isActive: boolean) => { - if (isActive) { - this.buttonRef!.current!.setAttribute( + const isInputDisabled = this.context === VIEW_STATE.PAUSE; + + if (isActive && !isInputDisabled && this.buttonRef.current) { + this.buttonRef.current.setAttribute( "class", "sensor-button active-button" ); - } else { + } else if (this.buttonRef.current) { this.buttonRef!.current!.setAttribute("class", "sensor-button"); } }; render() { + const isInputDisabled = this.context === VIEW_STATE.PAUSE; + return (