V4 updates for beta testing (#147)
* change simulator svg
* change radio image
* Remove google fonts cdn
* change color of 'advanced' button
* font fix
* font fix 2
* display fix
* change fullsceen simulator bg
* Continuous servo
* handle continuous state
* adding shims
* update rendering for continuous servos
* fixing sim
* fix sig
* typo
* fix sim
* bump pxt
* bump pxt
* rerun travis
* Input blocks revision
- add Button and Pin event types
- merge onPinPressed & onPinReleased in new onPinEvent function
- create new onButtonEvent function
* update input blocks in docs and tests
* remove device_pin_release block
* Hide DAL.x behind Enum
* bring back deprecated blocks, but hide them
* shims and locales files
* fix input.input. typing
* remove buildpr
* bump V3
* update simulator aspect ratio
* add Loudness Block
* revoke loudness block
* Adds soundLevel
To be replaced by pxt-common-packages when DAL is updated.
* Remove P0 & P3 from AnalogPin
* Fix Sound and replace AnalogPin.P0
* remove approved extensions
* V4 Updates from remote Repo
* locales
* add storage functions
* fix storage functions
* fix int/float values
* decrease decimal precision
* reorder blocks
* Update BLE Settings and Storage Blocks
* Fetch MicroBit changes up to v4.0.18
* Update timing for LED Matrix usage
* use 32kb ram (mini v2)
* resize gatt table
* Revert "use 32kb ram (mini v2)"
This reverts commit 4b15592f0f.
* fix missleading indentation
* add support for 32kb and 16kb ram
* only MIT extensions in preferredRepos
* remove extensions without MIT License file
* add updated extensions
* add extensions with MIT license
Co-authored-by: Juri <gitkraken@juriwolf.de>
Co-authored-by: Juri <info@juriwolf.de>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
namespace pxsim {
|
||||
export interface CommonBoard extends CoreBoard
|
||||
, EventBusBoard {
|
||||
bus: EventBus;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
}
|
||||
}
|
||||
+39
-23
@@ -4,7 +4,11 @@
|
||||
|
||||
namespace pxsim {
|
||||
export class DalBoard extends CoreBoard
|
||||
implements RadioBoard, LightBoard {
|
||||
implements CommonBoard
|
||||
, RadioBoard
|
||||
, LightBoard
|
||||
, MicrophoneBoard
|
||||
, ControlMessageBoard {
|
||||
// state & update logic for component services
|
||||
ledMatrixState: LedMatrixState;
|
||||
edgeConnectorState: EdgeConnectorState;
|
||||
@@ -20,17 +24,24 @@ namespace pxsim {
|
||||
rgbLedState: number;
|
||||
speakerState: SpeakerState;
|
||||
fileSystem: FileSystemState;
|
||||
logoTouch: Button;
|
||||
speakerEnabled: boolean = true;
|
||||
controlMessageState: ControlMessageState;
|
||||
|
||||
// visual
|
||||
viewHost: visuals.BoardHost;
|
||||
view: SVGElement;
|
||||
|
||||
// board hardware version
|
||||
hardwareVersion = 1;
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
// components
|
||||
this.lightState = {};
|
||||
this.fileSystem = new FileSystemState();
|
||||
this.controlMessageState = new ControlMessageState(this);
|
||||
this.builtinParts["ledmatrix"] = this.ledMatrixState = new LedMatrixState(runtime);
|
||||
this.builtinParts["buttonpair"] = this.buttonPairState = new ButtonPairState({
|
||||
ID_BUTTON_A: DAL.MICROBIT_ID_BUTTON_A,
|
||||
@@ -41,8 +52,10 @@ namespace pxsim {
|
||||
});
|
||||
this.builtinParts["edgeconnector"] = this.edgeConnectorState = new EdgeConnectorState({
|
||||
pins: [
|
||||
DAL.MICROBIT_ID_IO_P12,
|
||||
DAL.MICROBIT_ID_IO_P0,
|
||||
DAL.MICROBIT_ID_IO_P1,
|
||||
DAL.MICROBIT_ID_IO_P16,
|
||||
DAL.MICROBIT_ID_IO_P2,
|
||||
DAL.MICROBIT_ID_IO_P3,
|
||||
DAL.MICROBIT_ID_IO_P4,
|
||||
@@ -52,12 +65,10 @@ namespace pxsim {
|
||||
DAL.MICROBIT_ID_IO_P8,
|
||||
DAL.MICROBIT_ID_IO_P9,
|
||||
DAL.MICROBIT_ID_IO_P10,
|
||||
DAL.MICROBIT_ID_IO_P11,
|
||||
DAL.MICROBIT_ID_IO_P12,
|
||||
DAL.MICROBIT_ID_IO_P11,
|
||||
DAL.MICROBIT_ID_IO_P13,
|
||||
DAL.MICROBIT_ID_IO_P14,
|
||||
DAL.MICROBIT_ID_IO_P15,
|
||||
DAL.MICROBIT_ID_IO_P16,
|
||||
0,
|
||||
0,
|
||||
DAL.MICROBIT_ID_IO_P19,
|
||||
@@ -71,13 +82,13 @@ namespace pxsim {
|
||||
"P3": DAL.MICROBIT_ID_IO_P16
|
||||
}
|
||||
});
|
||||
this.builtinParts["radio"] = this.radioState = new RadioState(runtime, {
|
||||
this.builtinParts["radio"] = this.radioState = new RadioState(runtime, this, {
|
||||
ID_RADIO: DAL.MICROBIT_ID_RADIO,
|
||||
RADIO_EVT_DATAGRAM: DAL.MICROBIT_RADIO_EVT_DATAGRAM
|
||||
});
|
||||
this.builtinParts["microphone"] = this.microphoneState = new AnalogSensorState(3001 /* DEVICE_ID_MICROPHONE */, 52, 120, 75, 96);
|
||||
this.builtinParts["microphone"] = this.microphoneState = new AnalogSensorState(3001 /* DEVICE_ID_MICROPHONE */, 0, 255, 75, 180);
|
||||
this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime);
|
||||
this.builtinParts["serial"] = this.serialState = new SerialState();
|
||||
this.builtinParts["serial"] = this.serialState = new SerialState(runtime, this);
|
||||
this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState();
|
||||
this.builtinParts["lightsensor"] = this.lightSensorState = new LightSensorState();
|
||||
this.builtinParts["compass"] = this.compassState = new CompassState();
|
||||
@@ -97,25 +108,14 @@ namespace pxsim {
|
||||
this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy);
|
||||
}
|
||||
|
||||
receiveMessage(msg: SimulatorMessage) {
|
||||
if (!runtime || runtime.dead) return;
|
||||
|
||||
switch (msg.type || "") {
|
||||
case "eventbus":
|
||||
const ev = <SimulatorEventBusMessage>msg;
|
||||
this.bus.queue(ev.id, ev.eventid, ev.value);
|
||||
break;
|
||||
case "serial":
|
||||
const data = (<SimulatorSerialMessage>msg).data || "";
|
||||
this.serialState.receiveData(data);
|
||||
break;
|
||||
case "radiopacket":
|
||||
const packet = <SimulatorRadioPacketMessage>msg;
|
||||
this.radioState.receivePacket(packet);
|
||||
break;
|
||||
ensureHardwareVersion(version: number) {
|
||||
if (version > this.hardwareVersion) {
|
||||
this.hardwareVersion = version;
|
||||
this.updateView();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
initAsync(msg: SimulatorRunMessage): Promise<void> {
|
||||
super.initAsync(msg);
|
||||
|
||||
@@ -124,6 +124,21 @@ namespace pxsim {
|
||||
const cmpDefs = msg.partDefinitions || {};
|
||||
const fnArgs = msg.fnArgs;
|
||||
|
||||
const v2Parts: pxt.Map<boolean> = {
|
||||
"microphone": true,
|
||||
"logotouch": true,
|
||||
"builtinspeaker": true,
|
||||
"v2": true
|
||||
};
|
||||
if (msg.builtinParts) {
|
||||
const v2PartsUsed = msg.builtinParts.filter(k => v2Parts[k])
|
||||
if (v2PartsUsed.length) {
|
||||
console.log(`detected v2 feature`, v2PartsUsed);
|
||||
cmpsList.push(...v2PartsUsed);
|
||||
this.hardwareVersion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
const opts: visuals.BoardHostOpts = {
|
||||
state: this,
|
||||
boardDef: boardDef,
|
||||
@@ -134,6 +149,7 @@ namespace pxsim {
|
||||
maxHeight: "100%",
|
||||
highContrast: msg.highContrast
|
||||
};
|
||||
|
||||
this.viewHost = new visuals.BoardHost(pxsim.visuals.mkBoardView({
|
||||
visual: boardDef.visual,
|
||||
boardDef: boardDef,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="#009900" stroke="none" d="M13.922 2.495V19.3a2.35 2.35 0 11-3.845 0V2.495c0-.826.67-1.495 1.495-1.495h.855c.825 0 1.495.67 1.495 1.495zm-1.923 16.723a1.431 1.431 0 100 2.863 1.431 1.431 0 000-2.863zm5.426.001a1.431 1.431 0 100 2.863 1.431 1.431 0 000-2.863zm1.922-14.587V19.3a2.35 2.35 0 11-3.844 0V4.632c0-.826.67-1.495 1.495-1.495h.854c.826 0 1.495.67 1.495 1.495zM7.002 1.002c.826 0 1.495.669 1.495 1.494V19.3a2.35 2.35 0 11-3.848.006l.004-.006V2.496C4.653 1.67 5.323 1 6.148 1zM6.575 19.22a1.431 1.431 0 100 2.862 1.431 1.431 0 000-2.862z"/></svg>
|
||||
|
After Width: | Height: | Size: 646 B |
+11
-1
@@ -1,9 +1,19 @@
|
||||
CACHE MANIFEST
|
||||
|
||||
CACHE:
|
||||
/cdn/bluebird.min.js
|
||||
/cdn/pxtsim.js
|
||||
/sim/sim.js
|
||||
|
||||
/sim/sounds/giggle.wav
|
||||
/sim/sounds/happy.wav
|
||||
/sim/sounds/hello.wav
|
||||
/sim/sounds/mysterious.wav
|
||||
/sim/sounds/sad.wav
|
||||
/sim/sounds/slide.wav
|
||||
/sim/sounds/soaring.wav
|
||||
/sim/sounds/spring.wav
|
||||
/sim/sounds/twinkle.wav
|
||||
/sim/sounds/yawn.wav
|
||||
|
||||
NETWORK:
|
||||
*
|
||||
|
||||
@@ -19,10 +19,22 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="/cdn/bluebird.min.js"></script>
|
||||
<script src="/cdn/pxtsim.js"></script>
|
||||
<script src="/sim/sim.js"></script>
|
||||
</head>
|
||||
|
||||
<script>
|
||||
pxsim.soundExpressionFiles = {
|
||||
"giggle": "/sim/sounds/giggle.wav",
|
||||
"happy": "/sim/sounds/happy.wav",
|
||||
"hello": "/sim/sounds/hello.wav",
|
||||
"mysterious": "/sim/sounds/mysterious.wav",
|
||||
"sad": "/sim/sounds/sad.wav",
|
||||
"slide": "/sim/sounds/slide.wav",
|
||||
"soaring": "/sim/sounds/soaring.wav",
|
||||
"spring": "/sim/sounds/spring.wav",
|
||||
"twinkle": "/sim/sounds/twinkle.wav",
|
||||
"yawn": "/sim/sounds/yawn.wav"
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,14 @@
|
||||
namespace pxsim.input {
|
||||
export function onButtonEvent(button: number, buttonEvent: number, handler: RefAction): void {
|
||||
let b = board().buttonPairState;
|
||||
if (button == b.props.ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
b.usesButtonAB = true;
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
pxtcore.registerWithDal(button, buttonEvent, handler);
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
export function onButtonPressed(button: number, handler: RefAction): void {
|
||||
let b = board().buttonPairState;
|
||||
if (button == b.props.ID_BUTTON_AB && !b.usesButtonAB) {
|
||||
|
||||
@@ -8,8 +8,22 @@ namespace pxsim.input {
|
||||
return b.heading;
|
||||
}
|
||||
|
||||
export function assumeCalibrationCompass(){
|
||||
}
|
||||
|
||||
export function clearCalibrationCompass(){
|
||||
}
|
||||
|
||||
export function isCalibratedCompass(): boolean {
|
||||
// let b = board().compassState;
|
||||
// return b.isCalibrated;
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
export function magneticForce(): number {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
+60
-19
@@ -1,17 +1,27 @@
|
||||
namespace pxsim.input {
|
||||
export function onPinTouchEvent(pinId: number, pinEvent: number, handler: RefAction) {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, pinEvent, handler);
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
export function onPinPressed(pinId: number, handler: RefAction) {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_CLICK, handler);
|
||||
}
|
||||
|
||||
// Deprecated
|
||||
export function onPinReleased(pinId: number, handler: RefAction) {
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
pin.isTouched();
|
||||
runtime.queueDisplayUpdate();
|
||||
runtime.queueDisplayUpdate();
|
||||
pxtcore.registerWithDal(pin.id, DAL.MICROBIT_BUTTON_EVT_UP, handler);
|
||||
}
|
||||
|
||||
@@ -95,51 +105,68 @@ namespace pxsim.pins {
|
||||
}
|
||||
|
||||
export function analogSetPitchPin(pinId: number) {
|
||||
const b = board();
|
||||
if (!b) return;
|
||||
let pin = getPin(pinId);
|
||||
if (!pin) return;
|
||||
board().edgeConnectorState.pins.filter(p => !!p).forEach(p => p.pitch = false);
|
||||
const ec = b.edgeConnectorState
|
||||
ec.pins.filter(p => !!p).forEach(p => p.pitch = false);
|
||||
pin.pitch = true;
|
||||
}
|
||||
|
||||
export function setSoundOutputPinEnabled(enabled: boolean) {
|
||||
const b = board();
|
||||
if (!b) return;
|
||||
const ec = b.edgeConnectorState
|
||||
ec.pitchEnabled = !enabled;
|
||||
}
|
||||
|
||||
export function analogSetPitchVolume(volume: number) {
|
||||
const ec = board().edgeConnectorState;
|
||||
ec.pitchVolume = Math.max(0, Math.min(0xff, volume | 0));
|
||||
AudioContextManager.setCurrentToneGain((ec.pitchVolume / 0xff) / 10);
|
||||
}
|
||||
|
||||
export function analogPitchVolume() {
|
||||
const ec = board().edgeConnectorState;
|
||||
return ec.pitchVolume;
|
||||
}
|
||||
|
||||
|
||||
export function analogPitch(frequency: number, ms: number) {
|
||||
// update analog output
|
||||
const b = board();
|
||||
if (!b) return;
|
||||
const ec = b.edgeConnectorState;
|
||||
const pins = ec.pins;
|
||||
const pin = pins.filter(pin => !!pin && pin.pitch)[0] || pins[0];
|
||||
const pin = ec.pitchEnabled && (pins.filter(pin => !!pin && pin.pitch)[0] || pins[0]);
|
||||
const pitchVolume = ec.pitchVolume | 0;
|
||||
pin.mode = PinFlags.Analog | PinFlags.Output;
|
||||
if (frequency <= 0 || pitchVolume <= 0) {
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
} else {
|
||||
const v = 1 << (pitchVolume >> 5);
|
||||
pin.value = v;
|
||||
pin.period = 1000000 / frequency;
|
||||
if (pin) {
|
||||
pin.mode = PinFlags.Analog | PinFlags.Output;
|
||||
if (frequency <= 0 || pitchVolume <= 0) {
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
} else {
|
||||
const v = 1 << (pitchVolume >> 5);
|
||||
pin.value = v;
|
||||
pin.period = 1000000 / frequency;
|
||||
}
|
||||
runtime.queueDisplayUpdate();
|
||||
}
|
||||
runtime.queueDisplayUpdate();
|
||||
|
||||
let cb = getResume();
|
||||
const v = pitchVolume / 0xff;
|
||||
AudioContextManager.tone(frequency, v);
|
||||
if (pin) {
|
||||
const v = pitchVolume / 0xff;
|
||||
AudioContextManager.tone(frequency, v / 10);
|
||||
}
|
||||
if (ms <= 0) cb();
|
||||
else {
|
||||
setTimeout(() => {
|
||||
AudioContextManager.stop();
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
pin.mode = PinFlags.Unused;
|
||||
if (pin) {
|
||||
pin.value = 0;
|
||||
pin.period = 0;
|
||||
pin.mode = PinFlags.Unused;
|
||||
}
|
||||
runtime.queueDisplayUpdate();
|
||||
cb()
|
||||
}, ms);
|
||||
@@ -152,4 +179,18 @@ namespace pxsim.pins {
|
||||
const ec = b.edgeConnectorState;
|
||||
// TODO support buttons here
|
||||
}
|
||||
}
|
||||
namespace pxsim.music {
|
||||
export function setVolume(volume: number): void {
|
||||
pxsim.pins.analogSetPitchVolume(volume);
|
||||
}
|
||||
export function volume(): number {
|
||||
return pxsim.pins.analogPitchVolume();
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.pins {
|
||||
export function setAudioPin(pinId: number) {
|
||||
pxsim.pins.analogSetPitchPin(pinId);
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,7 @@ namespace pxsim {
|
||||
export class EdgeConnectorState {
|
||||
pins: Pin[];
|
||||
pitchVolume: number;
|
||||
pitchEnabled = true;
|
||||
|
||||
constructor(public props: EdgeConnectorProps) {
|
||||
this.pins = props.pins.map(id => id != undefined ? new Pin(id) : null);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// move to common packages eventually
|
||||
namespace pxsim.input {
|
||||
|
||||
export function soundLevel(): number {
|
||||
const b = microphoneState();
|
||||
if (!b) return 0;
|
||||
b.setUsed();
|
||||
return b.getLevel();
|
||||
}
|
||||
|
||||
export function onSound(sound: number /* SoundThreshold */, body: RefAction) {
|
||||
const b = microphoneState();
|
||||
if (!b) return;
|
||||
b.setUsed();
|
||||
pxtcore.registerWithDal(b.id, sound, body);
|
||||
}
|
||||
|
||||
export function setSoundThreshold(sound: number, threshold: number){
|
||||
const b = microphoneState();
|
||||
if (!b) return;
|
||||
b.setUsed();
|
||||
if (sound === 2 /* SoundThreshold.Loud */)
|
||||
b.setHighThreshold(threshold);
|
||||
else
|
||||
b.setLowThreshold(threshold);
|
||||
}
|
||||
|
||||
}
|
||||
+17
-51
@@ -35,46 +35,6 @@ namespace pxsim.basic {
|
||||
namespace pxsim.control {
|
||||
export var inBackground = thread.runInBackground;
|
||||
|
||||
export function createBuffer(sz: number) {
|
||||
return pxsim.BufferMethods.createBuffer(sz)
|
||||
}
|
||||
|
||||
export function reset() {
|
||||
const cb = getResume();
|
||||
pxsim.runtime.restart();
|
||||
}
|
||||
|
||||
export function waitMicros(micros: number) {
|
||||
// TODO
|
||||
}
|
||||
export function waitForEvent(id: number, evid: number) {
|
||||
const cb = getResume();
|
||||
board().bus.wait(id, evid, cb);
|
||||
}
|
||||
|
||||
export function millis(): number {
|
||||
return runtime.runningTime();
|
||||
}
|
||||
|
||||
export function micros(): number {
|
||||
return runtime.runningTimeUs();
|
||||
}
|
||||
|
||||
|
||||
export function deviceName(): string {
|
||||
let b = board();
|
||||
return b && b.id
|
||||
? b.id.slice(0, 4)
|
||||
: "abcd";
|
||||
}
|
||||
|
||||
export function deviceSerialNumber(): number {
|
||||
let b = board();
|
||||
return parseInt(b && b.id
|
||||
? b.id.slice(1)
|
||||
: "42");
|
||||
}
|
||||
|
||||
export function onEvent(id: number, evid: number, handler: RefAction) {
|
||||
if (id == DAL.MICROBIT_ID_BUTTON_AB) {
|
||||
const b = board().buttonPairState;
|
||||
@@ -86,11 +46,6 @@ namespace pxsim.control {
|
||||
pxtcore.registerWithDal(id, evid, handler)
|
||||
}
|
||||
|
||||
export function raiseEvent(id: number, evid: number, mode: number) {
|
||||
// TODO mode?
|
||||
board().bus.queue(id, evid)
|
||||
}
|
||||
|
||||
export function eventTimestamp() {
|
||||
return board().bus.getLastEventTime()
|
||||
}
|
||||
@@ -100,12 +55,6 @@ namespace pxsim.control {
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.pxtcore {
|
||||
export function registerWithDal(id: number, evid: number, handler: RefAction) {
|
||||
board().bus.listen(id, evid, handler);
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.input {
|
||||
export function calibrateCompass() {
|
||||
// device calibrates...
|
||||
@@ -168,6 +117,12 @@ namespace pxsim.pins {
|
||||
|
||||
export function setEvents(name: number, event: number) {
|
||||
}
|
||||
|
||||
export function setMatrixWidth(pin: number, width: number) {
|
||||
const lp = neopixelState(pin);
|
||||
if (!lp) return;
|
||||
lp.width = width;
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.devices {
|
||||
@@ -246,13 +201,24 @@ namespace pxsim.bluetooth {
|
||||
}
|
||||
|
||||
namespace pxsim.light {
|
||||
|
||||
export function sendWS2812Buffer(buffer: RefBuffer, pin: number) {
|
||||
pxsim.sendBufferAsm(buffer, pin)
|
||||
}
|
||||
|
||||
export function sendWS2812BufferWithBrightness(buffer: RefBuffer, pin: number, brightness: number) {
|
||||
const clone = new RefBuffer(new Uint8Array(buffer.data))
|
||||
const data = clone.data;
|
||||
for(let i =0; i < data.length; ++i) {
|
||||
data[i] = (data[i] * brightness) >> 8;
|
||||
}
|
||||
pxsim.sendBufferAsm(clone, pin)
|
||||
}
|
||||
|
||||
export function setMode(pin: number, mode: number) {
|
||||
const lp = neopixelState(pin);
|
||||
if (!lp) return;
|
||||
lp.mode = mode & 0xff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-6
@@ -3,6 +3,17 @@ namespace pxsim {
|
||||
export class SerialState {
|
||||
serialIn: string[] = [];
|
||||
|
||||
constructor(private readonly runtime: Runtime, private readonly board: BaseBoard) {
|
||||
this.board.addMessageListener(this.handleMessage.bind(this))
|
||||
}
|
||||
|
||||
private handleMessage(msg: SimulatorMessage) {
|
||||
if (msg.type === "serial") {
|
||||
const data = (<SimulatorSerialMessage>msg).data || "";
|
||||
this.receiveData(data);
|
||||
}
|
||||
}
|
||||
|
||||
public receiveData(data: string) {
|
||||
this.serialIn.push();
|
||||
}
|
||||
@@ -28,12 +39,6 @@ namespace pxsim {
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.control {
|
||||
export function __log(s: string) {
|
||||
board().writeSerial(s + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
namespace pxsim.serial {
|
||||
export function writeString(s: string) {
|
||||
board().writeSerial(s);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace pxsim.music {
|
||||
function loadWavAsync(path: string): Promise<Uint8Array> {
|
||||
return new Promise<Uint8Array>((resolve, reject) => {
|
||||
let httprequest = new XMLHttpRequest();
|
||||
httprequest.responseType = "arraybuffer";
|
||||
httprequest.onreadystatechange = function () {
|
||||
if (httprequest.readyState == XMLHttpRequest.DONE) {
|
||||
if (httprequest.status == 200) {
|
||||
const r = httprequest.response;
|
||||
resolve(new Uint8Array(httprequest.response));
|
||||
}
|
||||
else {
|
||||
reject(httprequest.status);
|
||||
}
|
||||
}
|
||||
};
|
||||
httprequest.open("GET", path, true);
|
||||
httprequest.send();
|
||||
})
|
||||
}
|
||||
const wavPromises: Map<Promise<Uint8Array>> = {}
|
||||
//%
|
||||
export function __playSoundExpression(notes: string, waitTillDone: boolean): void {
|
||||
const cb = getResume();
|
||||
const b = board();
|
||||
// v2 only...
|
||||
b.ensureHardwareVersion(2);
|
||||
|
||||
// load wav file
|
||||
let p: Promise<Uint8Array>;
|
||||
// defined in sim.html
|
||||
const path = (<any>pxsim).soundExpressionFiles[notes];
|
||||
if (path) {
|
||||
p = wavPromises[notes] || (wavPromises[notes] = loadWavAsync(path));
|
||||
} else
|
||||
p = Promise.resolve(undefined);
|
||||
|
||||
p.then(data => {
|
||||
// failed to load data
|
||||
if (data) {
|
||||
// finally play
|
||||
const buf = new RefBuffer(data);
|
||||
const pp = AudioContextManager.playBufferAsync(buf)
|
||||
if (waitTillDone)
|
||||
// wait until sound is done
|
||||
return pp;
|
||||
}
|
||||
// don't wait
|
||||
cb();
|
||||
return Promise.resolve();
|
||||
}).catch((e) => {
|
||||
console.log(e)
|
||||
cb();
|
||||
})
|
||||
}
|
||||
|
||||
export function __stopSoundExpressions() {
|
||||
AudioContextManager.stopAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
namespace pxsim.storage {
|
||||
|
||||
export function putValue(key: string, value: string) : void {
|
||||
sessionStorage.setItem('simulatorValue_'+key, value);
|
||||
}
|
||||
|
||||
export function putValueInt(key: string, value: number) : void {
|
||||
sessionStorage.setItem('simulatorValue_'+key, value+"");
|
||||
}
|
||||
|
||||
export function getValue(key: string) : string {
|
||||
if(sessionStorage.getItem('simulatorValue_'+key)) {
|
||||
return sessionStorage.getItem('simulatorValue_'+key);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function getValueInt(key: string) : number {
|
||||
if(sessionStorage.getItem('simulatorValue_'+key)) {
|
||||
return parseFloat(sessionStorage.getItem('simulatorValue_'+key));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export function removeStr(key: string) : void {
|
||||
sessionStorage.removeItem('simulatorValue_'+key);
|
||||
}
|
||||
|
||||
export function removeInt(key: string) : void {
|
||||
sessionStorage.removeItem('simulatorValue_'+key);
|
||||
}
|
||||
|
||||
}
|
||||
+13
-5
@@ -1,23 +1,31 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es2017",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"declaration": true,
|
||||
"out": "../built/sim.js",
|
||||
"rootDir": ".",
|
||||
"newLine": "LF",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": false,
|
||||
"lib": ["dom", "dom.iterable", "scripthost", "es6"],
|
||||
"types": ["bluebird"],
|
||||
"typeRoots": ["../node_modules/@types"]
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost",
|
||||
"es2017"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"state/*.ts",
|
||||
"visuals/*.ts",
|
||||
"../node_modules/pxt-common-packages/libs/base/sim/control.ts",
|
||||
"../node_modules/pxt-common-packages/libs/base/sim/controlmessage.ts",
|
||||
"../node_modules/pxt-common-packages/libs/core/sim/analogSensor.ts",
|
||||
"../node_modules/pxt-common-packages/libs/microphone/sim/state.ts",
|
||||
"../node_modules/pxt-common-packages/libs/core/sim/neopixel.ts",
|
||||
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts"
|
||||
"../node_modules/pxt-common-packages/libs/radio/sim/*.ts",
|
||||
"../node_modules/pxt-common-packages/libs/settings/sim/*.ts"
|
||||
]
|
||||
}
|
||||
|
||||
+499
-131
@@ -8,10 +8,15 @@ namespace pxsim.visuals {
|
||||
-webkit-filter: grayscale(1);
|
||||
filter: grayscale(1);
|
||||
}
|
||||
.sim-button-group {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sim-button {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sim-board, .sim-display, sim-button {
|
||||
fill: #111;
|
||||
}
|
||||
.sim-button-outer:hover {
|
||||
stroke:grey;
|
||||
stroke-width: 3px;
|
||||
@@ -59,20 +64,21 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
.sim-text {
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:14px;
|
||||
fill:#fff;
|
||||
pointer-events: none; user-select: none;
|
||||
}
|
||||
.sim-text.inverted {
|
||||
fill:#000;
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:14px;
|
||||
fill:#fff;
|
||||
pointer-events: none; user-select: none;
|
||||
}
|
||||
|
||||
.sim-text-pin {
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
font-size:20px;
|
||||
fill:#3cb5b5;
|
||||
pointer-events: none;
|
||||
font-family:"Lucida Console", Monaco, monospace;
|
||||
pointer-events: none; user-select: none;
|
||||
fill:#3cb5b5;
|
||||
font-size:24px;
|
||||
stroke:#fff;
|
||||
stroke-alignment: outside;
|
||||
paint-order: stroke;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
|
||||
.sim-thermometer {
|
||||
@@ -84,6 +90,22 @@ namespace pxsim.visuals {
|
||||
r:8px;
|
||||
}
|
||||
|
||||
.inverted {
|
||||
fill:#000;
|
||||
stroke:#fff;
|
||||
stroke-alignment: outside;
|
||||
paint-order: stroke;
|
||||
stroke-width: 3px;
|
||||
}
|
||||
.big {
|
||||
font-size:24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.centered {
|
||||
transform: translateX(-1.5ch);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* animations */
|
||||
.sim-theme-glow {
|
||||
animation-name: sim-theme-glow-animation;
|
||||
@@ -686,6 +708,8 @@ namespace pxsim.visuals {
|
||||
virtualButtonDown?: string;
|
||||
lightLevelOn?: string;
|
||||
lightLevelOff?: string;
|
||||
soundLevelOn?: string;
|
||||
soundLevelOff?: string;
|
||||
}
|
||||
|
||||
export var themes: IBoardTheme[] = ["#3ADCFE"].map(accent => {
|
||||
@@ -702,8 +726,10 @@ namespace pxsim.visuals {
|
||||
virtualButtonDown: "#FFA500",
|
||||
virtualButtonOuter: "#333",
|
||||
virtualButtonUp: "#fff",
|
||||
lightLevelOn: "yellow",
|
||||
lightLevelOff: "#555"
|
||||
lightLevelOn: "#555",
|
||||
lightLevelOff: "yellow",
|
||||
soundLevelOn: "#3ADCFE",
|
||||
soundLevelOff: "#555"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -723,14 +749,24 @@ namespace pxsim.visuals {
|
||||
private style: SVGStyleElement;
|
||||
private defs: SVGDefsElement;
|
||||
private g: SVGGElement;
|
||||
|
||||
private pkg: SVGPathElement;
|
||||
private logos: SVGElement[];
|
||||
private headg: SVGGElement;
|
||||
private head: SVGGElement;
|
||||
private headParts: SVGElement;
|
||||
private headInitialized = false;
|
||||
private heads: SVGElement[];
|
||||
private headText: SVGTextElement;
|
||||
private display: SVGElement;
|
||||
private buttons: SVGElement[];
|
||||
private buttonsOuter: SVGElement[];
|
||||
private buttonABText: SVGTextElement;
|
||||
private pins: SVGElement[];
|
||||
private pinGradients: SVGLinearGradientElement[];
|
||||
private pinTexts:{ [key: number]: SVGTextElement };
|
||||
private ledsOuter: SVGElement[];
|
||||
private leds: SVGElement[];
|
||||
private microphoneLed: SVGElement;
|
||||
private systemLed: SVGCircleElement;
|
||||
private antenna: SVGPolylineElement;
|
||||
private rssi: SVGTextElement;
|
||||
@@ -740,8 +776,14 @@ namespace pxsim.visuals {
|
||||
private thermometerGradient: SVGLinearGradientElement;
|
||||
private thermometer: SVGRectElement;
|
||||
private thermometerText: SVGTextElement;
|
||||
private soundLevelGradient: SVGLinearGradientElement;
|
||||
private soundLevel: SVGRectElement;
|
||||
private soundLevelText: SVGTextElement;
|
||||
private soundLevelIcon: SVGTextElement;
|
||||
private shakeButton: SVGElement;
|
||||
private shakeText: SVGTextElement;
|
||||
public board: pxsim.DalBoard;
|
||||
private domHardwareVersion = 1;
|
||||
private rgbLed: SVGElement;
|
||||
private pinNmToCoord: Map<Coord> = {
|
||||
"EXT_PWR": [
|
||||
@@ -1008,7 +1050,7 @@ namespace pxsim.visuals {
|
||||
|
||||
this.pinGradients.forEach(lg => svg.setGradientColors(lg, theme.pin, theme.pinActive));
|
||||
svg.setGradientColors(this.lightLevelGradient, theme.lightLevelOn, theme.lightLevelOff);
|
||||
|
||||
svg.setGradientColors(this.soundLevelGradient, theme.soundLevelOff, theme.soundLevelOn);
|
||||
svg.setGradientColors(this.thermometerGradient, theme.ledOff, theme.ledOn);
|
||||
}
|
||||
|
||||
@@ -1017,6 +1059,18 @@ namespace pxsim.visuals {
|
||||
if (!state) return;
|
||||
let theme = this.props.theme;
|
||||
|
||||
this.updateMicrophone();
|
||||
this.updatePins();
|
||||
this.updateTilt();
|
||||
this.updateHeading();
|
||||
this.updateLightLevel();
|
||||
this.updateTemperature();
|
||||
this.updateButtonAB();
|
||||
this.updateGestures();
|
||||
this.updateRgbLed();
|
||||
this.updateSpeaker();
|
||||
this.updateRSSI();
|
||||
|
||||
let bpState = state.buttonPairState;
|
||||
let buttons = [bpState.aBtn, bpState.bBtn, bpState.abBtn];
|
||||
buttons.forEach((btn, index) => {
|
||||
@@ -1046,16 +1100,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
})
|
||||
}
|
||||
this.updatePins();
|
||||
this.updateTilt();
|
||||
this.updateHeading();
|
||||
this.updateLightLevel();
|
||||
this.updateTemperature();
|
||||
this.updateButtonAB();
|
||||
this.updateGestures();
|
||||
this.updateRgbLed();
|
||||
this.updateSpeaker();
|
||||
this.updateRSSI();
|
||||
|
||||
|
||||
if (!runtime || runtime.dead) U.addClass(this.element, "grayscale");
|
||||
else U.removeClass(this.element, "grayscale");
|
||||
@@ -1090,7 +1135,7 @@ namespace pxsim.visuals {
|
||||
private updateGestures() {
|
||||
let state = this.board;
|
||||
if (state.accelerometerState.useShake && !this.shakeButton) {
|
||||
let shake = this.mkBtn(26, MB_HEIGHT - 45);
|
||||
let shake = this.mkBtn(280, MB_HEIGHT - 45);
|
||||
this.shakeButton = shake.inner;
|
||||
svg.fill(this.shakeButton, this.props.theme.virtualButtonUp)
|
||||
svg.buttonEvents(shake.outer,
|
||||
@@ -1103,11 +1148,17 @@ namespace pxsim.visuals {
|
||||
this.board.bus.queue(DAL.MICROBIT_ID_GESTURE, 11); // GESTURE_SHAKE
|
||||
}
|
||||
)
|
||||
let shakeText = svg.child(shake.outer, "text", { x: 15, y: MB_HEIGHT - 10, class: "sim-text inverted" }) as SVGTextElement;
|
||||
let shakeText = svg.child(shake.outer, "text", { x: 280, y: MB_HEIGHT - 5, class: "sim-text big inverted centered" }) as SVGTextElement;
|
||||
shakeText.textContent = "SHAKE"
|
||||
}
|
||||
}
|
||||
|
||||
private updateMicrophone() {
|
||||
const b = board();
|
||||
if (!b || !b.microphoneState.sensorUsed) return;
|
||||
this.updateSoundLevel();
|
||||
}
|
||||
|
||||
private updateButtonAB() {
|
||||
let state = this.board;
|
||||
if (state.buttonPairState.usesButtonAB && (<any>this.buttons[2]).style.visibility != "visible") {
|
||||
@@ -1158,7 +1209,7 @@ namespace pxsim.visuals {
|
||||
}
|
||||
else if (pin.mode & PinFlags.Touch) {
|
||||
v = pin.touched ? "0%" : "100%";
|
||||
if (text) text.textContent = "";
|
||||
if (text) text.textContent = v;
|
||||
} else {
|
||||
v = "100%";
|
||||
if (text) text.textContent = "";
|
||||
@@ -1185,65 +1236,218 @@ namespace pxsim.visuals {
|
||||
if (!this.thermometer) {
|
||||
let gid = "gradient-thermometer";
|
||||
this.thermometerGradient = svg.linearGradient(this.defs, gid);
|
||||
const ty = MB_HEIGHT - 180;
|
||||
const ty = MB_HEIGHT - 200;
|
||||
this.thermometer = <SVGRectElement>svg.child(this.g, "rect", {
|
||||
class: "sim-thermometer",
|
||||
x: 28,
|
||||
x: 0,
|
||||
y: ty,
|
||||
width: 10,
|
||||
height: 80,
|
||||
rx: 5, ry: 5,
|
||||
width: 30,
|
||||
height: 160,
|
||||
rx: 5,
|
||||
ry: 5,
|
||||
fill: `url(#${gid})`
|
||||
});
|
||||
this.thermometerText = svg.child(this.g, "text", {
|
||||
class: 'sim-text',
|
||||
x: 48, y: ty + 78
|
||||
class: 'sim-text big inverted centered',
|
||||
x: 15,
|
||||
y: ty + 190
|
||||
}) as SVGTextElement;
|
||||
this.updateTheme();
|
||||
|
||||
let pt = this.element.createSVGPoint();
|
||||
svg.buttonEvents(this.thermometer,
|
||||
// move
|
||||
(ev) => {
|
||||
let cur = svg.cursorPoint(pt, this.element, ev);
|
||||
let t = Math.max(0, Math.min(1, (cur.y - ty - 5) / 70))
|
||||
let t = Math.max(0, Math.min(1, (cur.y - ty) / 160))
|
||||
state.thermometerState.temperature = Math.floor(tmax - t * (tmax - tmin));
|
||||
this.updateTemperature();
|
||||
}, ev => { }, ev => { })
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
(ev) => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.thermometerState.temperature--;
|
||||
if(state.thermometerState.temperature < tmin) state.thermometerState.temperature = tmin;
|
||||
this.updateTemperature();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.thermometerState.temperature++
|
||||
if(state.thermometerState.temperature > tmax) state.thermometerState.temperature = tmax;
|
||||
this.updateTemperature();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
accessibility.makeFocusable(this.thermometer);
|
||||
accessibility.setAria(this.thermometer, "slider", pxsim.localization.lf("Temperature Level"));
|
||||
this.thermometer.setAttribute("aria-valuemin", tmin + "");
|
||||
this.thermometer.setAttribute("aria-valuemax", tmax + "");
|
||||
this.thermometer.setAttribute("aria-orientation", "vertical");
|
||||
this.thermometer.setAttribute("aria-valuenow", state.thermometerState.temperature + "");
|
||||
this.thermometer.setAttribute("aria-valuetext", state.thermometerState.temperature + "");
|
||||
|
||||
let t = Math.max(tmin, Math.min(tmax, state.thermometerState.temperature))
|
||||
let per = Math.floor((state.thermometerState.temperature - tmin) / (tmax - tmin) * 100)
|
||||
svg.setGradientValue(this.thermometerGradient, 100 - per + "%");
|
||||
this.thermometerText.textContent = t + "°C";
|
||||
this.thermometer.setAttribute("aria-valuenow", t.toString());
|
||||
this.thermometer.setAttribute("aria-valuetext", t + "°C");
|
||||
accessibility.setLiveContent(t + "°C");
|
||||
}
|
||||
|
||||
private updateSoundLevel() {
|
||||
let state = this.board;
|
||||
if (!state || !state.microphoneState.sensorUsed) return;
|
||||
|
||||
const tmin = 0 // state.microphoneState.min;
|
||||
const tmax = 255 //state.microphoneState.max;
|
||||
if (!this.soundLevel) {
|
||||
const level = state.microphoneState.getLevel();
|
||||
let gid = "gradient-soundlevel";
|
||||
this.soundLevelGradient = svg.linearGradient(this.defs, gid);
|
||||
const ty = MB_HEIGHT - 200;
|
||||
this.soundLevel = <SVGRectElement>svg.child(this.g, "rect", {
|
||||
class: "sim-thermometer",
|
||||
x: 490,
|
||||
y: ty,
|
||||
width: 30,
|
||||
height: 160,
|
||||
rx: 5,
|
||||
ry: 5,
|
||||
fill: `url(#${gid})`
|
||||
});
|
||||
this.soundLevelText = svg.child(this.g, "text", {
|
||||
class: 'sim-text big inverted centered',
|
||||
x: 505,
|
||||
y: ty + 190
|
||||
}) as SVGTextElement;
|
||||
this.soundLevelIcon = svg.child(this.g, "svg", {
|
||||
x: 495,
|
||||
y: 400,
|
||||
viewbox: "0 0 20 29",
|
||||
role: "img",
|
||||
}) as SVGTextElement;
|
||||
this.soundLevelIcon.setAttribute("aria-hidden", "true");
|
||||
this.soundLevelIcon.setAttribute("focusable", "false");
|
||||
this.soundLevelIcon.setAttribute("style", "pointer-events: none; opacity: 0.8; width: 20px;");
|
||||
svg.child(this.soundLevelIcon, "path", {
|
||||
fill: "white",
|
||||
d: "M 10 19.9375 C 13.011719 19.9375 15.453125 17.503906 15.453125 14.5 L 15.453125 5.4375 C 15.453125 2.433594 13.011719 0 10 0 C 6.988281 0 4.546875 2.433594 4.546875 5.4375 L 4.546875 14.5 C 4.546875 17.503906 6.988281 19.9375 10 19.9375 Z M 19.089844 10.875 L 18.183594 10.875 C 17.679688 10.875 17.273438 11.28125 17.273438 11.78125 L 17.273438 14.5 C 17.273438 18.738281 13.609375 22.136719 9.273438 21.714844 C 5.496094 21.347656 2.726562 17.960938 2.726562 14.175781 L 2.726562 11.78125 C 2.726562 11.28125 2.320312 10.875 1.816406 10.875 L 0.910156 10.875 C 0.40625 10.875 0 11.28125 0 11.78125 L 0 14.054688 C 0 19.132812 3.632812 23.660156 8.636719 24.347656 L 8.636719 26.28125 L 5.453125 26.28125 C 4.953125 26.28125 4.546875 26.6875 4.546875 27.1875 L 4.546875 28.09375 C 4.546875 28.59375 4.953125 29 5.453125 29 L 14.546875 29 C 15.046875 29 15.453125 28.59375 15.453125 28.09375 L 15.453125 27.1875 C 15.453125 26.6875 15.046875 26.28125 14.546875 26.28125 L 11.363281 26.28125 L 11.363281 24.367188 C 16.234375 23.703125 20 19.535156 20 14.5 L 20 11.78125 C 20 11.28125 19.59375 10.875 19.089844 10.875 Z M 19.089844 10.875 "
|
||||
});
|
||||
if (this.props.runtime)
|
||||
this.props.runtime.environmentGlobals[pxsim.localization.lf("sound level")] = state.microphoneState.getLevel();
|
||||
this.updateTheme();
|
||||
|
||||
let pt = this.element.createSVGPoint();
|
||||
svg.buttonEvents(this.soundLevel,
|
||||
// move
|
||||
(ev) => {
|
||||
let cur = svg.cursorPoint(pt, this.element, ev);
|
||||
let t = Math.max(0, Math.min(1, (cur.y - ty) / 160)) * tmax
|
||||
console.log(tmax - t);
|
||||
state.microphoneState.setLevel( Math.floor(tmax - t));
|
||||
// state.microphoneState.setLevel(Math.floor(tmin + t * (tmax - tmin)));
|
||||
this.updateMicrophone();
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
(ev) => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.microphoneState.setLevel(state.microphoneState.getLevel() - 1);
|
||||
if(state.microphoneState.getLevel() < tmin) state.microphoneState.setLevel(tmin);
|
||||
this.updateMicrophone();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.microphoneState.setLevel(state.microphoneState.getLevel() + 1);
|
||||
if(state.microphoneState.getLevel() > tmax) state.microphoneState.setLevel(tmax);
|
||||
this.updateMicrophone();
|
||||
}
|
||||
})
|
||||
|
||||
accessibility.makeFocusable(this.soundLevel);
|
||||
accessibility.setAria(this.soundLevel, "slider", pxsim.localization.lf("Sound Level"));
|
||||
this.soundLevel.setAttribute("aria-valuemin", tmin + "");
|
||||
this.soundLevel.setAttribute("aria-valuemax", tmax + "");
|
||||
this.soundLevel.setAttribute("aria-orientation", "vertical");
|
||||
this.soundLevel.setAttribute("aria-valuenow", level + "");
|
||||
this.soundLevel.setAttribute("aria-valuetext", level + "");
|
||||
}
|
||||
|
||||
let t = Math.max(tmin, Math.min(tmax, state.microphoneState.getLevel()))
|
||||
let per = Math.floor((state.microphoneState.getLevel() - tmin) / (tmax - tmin) * 100)
|
||||
svg.setGradientValue(this.soundLevelGradient, (100 - per) + "%");
|
||||
this.soundLevelText.textContent = t + "";
|
||||
this.soundLevel.setAttribute("aria-valuenow", t.toString());
|
||||
this.soundLevel.setAttribute("aria-valuetext", t + "");
|
||||
accessibility.setLiveContent(t + "");
|
||||
}
|
||||
|
||||
private updateHeading() {
|
||||
let xc = 258;
|
||||
const valMin = 0;
|
||||
const valMax = 360;
|
||||
let xc = 501.2;
|
||||
let yc = 75;
|
||||
let state = this.board;
|
||||
if (!state || !state.compassState.usesHeading) return;
|
||||
/*
|
||||
// /*
|
||||
if (!this.headInitialized) {
|
||||
let p = this.head.firstChild.nextSibling as SVGPathElement;
|
||||
p.setAttribute("d", "m269.9,50.134647l0,0l-39.5,0l0,0c-14.1,0.1 -24.6,10.7 -24.6,24.8c0,13.9 10.4,24.4 24.3,24.7l0,0l39.6,0c14.2,0 40.36034,-22.97069 40.36034,-24.85394c0,-1.88326 -26.06034,-24.54606 -40.16034,-24.64606m-0.2,39l0,0l-39.3,0c-7.7,-0.1 -14,-6.4 -14,-14.2c0,-7.8 6.4,-14.2 14.2,-14.2l39.1,0c7.8,0 14.2,6.4 14.2,14.2c0,7.9 -6.4,14.2 -14.2,14.2l0,0l0,0z");
|
||||
let p = this.heads[1];
|
||||
<SVGGElement>svg.child(p, "circle", {style: "fill:#DDDDDD55;stroke:#3A3A3A;", cx: "501.2", cy: "75", r: "55" });
|
||||
<SVGGElement>svg.child(p, "polyline", {style: "fill:#008EEF;stroke:#3A3A3A;", points: "517.7,75 501.1,140.2 484.6,75" });
|
||||
<SVGGElement>svg.child(p, "polyline", {style: "fill:#FF3951;stroke:#3A3A3A;", points: "484.6,75 501.1,9.5 517.7,75" });
|
||||
<SVGGElement>svg.child(p, "circle", {style: "fill:#748476;stroke:#3A3A3A;", cx: "501.1", cy: "75", r: "16.5" });
|
||||
<SVGGElement>svg.child(p, "circle", {style: "fill:#CCDBCE;", cx: "501.1", cy: "75", r: "10" });
|
||||
// p.setAttribute("d", "m269.9,50.134647l0,0l-39.5,0l0,0c-14.1,0.1 -24.6,10.7 -24.6,24.8c0,13.9 10.4,24.4 24.3,24.7l0,0l39.6,0c14.2,0 40.36034,-22.97069 40.36034,-24.85394c0,-1.88326 -26.06034,-24.54606 -40.16034,-24.64606m-0.2,39l0,0l-39.3,0c-7.7,-0.1 -14,-6.4 -14,-14.2c0,-7.8 6.4,-14.2 14.2,-14.2l39.1,0c7.8,0 14.2,6.4 14.2,14.2c0,7.9 -6.4,14.2 -14.2,14.2l0,0l0,0z");
|
||||
this.updateTheme();
|
||||
let pt = this.element.createSVGPoint();
|
||||
svg.buttonEvents(
|
||||
this.head,
|
||||
svg.buttonEvents(this.head,
|
||||
// move
|
||||
(ev: MouseEvent) => {
|
||||
let cur = svg.cursorPoint(pt, this.element, ev);
|
||||
state.compassState.heading = Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI + 90);
|
||||
if (state.compassState.heading < 0) state.compassState.heading += 360;
|
||||
state.compassState.heading = valMax - (Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI) + 90) - valMax;
|
||||
if (state.compassState.heading < valMin) state.compassState.heading += valMax;
|
||||
this.updateHeading();
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
ev => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.compassState.heading--;
|
||||
if(state.compassState.heading < valMin) state.compassState.heading += valMax;
|
||||
this.updateHeading();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.compassState.heading++;
|
||||
if(state.compassState.heading >= valMax) state.compassState.heading -= valMax;;
|
||||
this.updateHeading();
|
||||
}
|
||||
});
|
||||
this.headInitialized = true;
|
||||
}
|
||||
|
||||
accessibility.makeFocusable(this.head);
|
||||
accessibility.setAria(this.head, "slider", pxsim.localization.lf("Heading"));
|
||||
this.head.setAttribute("aria-valuemin", valMin + "");
|
||||
this.head.setAttribute("aria-valuemax", valMax + "");
|
||||
this.head.setAttribute("aria-orientation", "vertical");
|
||||
this.head.setAttribute("aria-valuenow", state.compassState.heading + "");
|
||||
this.head.setAttribute("aria-valuetext", state.compassState.heading + "");
|
||||
|
||||
let txt = state.compassState.heading.toString() + "°";
|
||||
if (txt != this.headText.textContent) {
|
||||
svg.rotateElement(this.head, xc, yc, state.compassState.heading + 180);
|
||||
svg.rotateElement(this.head, xc, yc, valMax - state.compassState.heading - 90);
|
||||
this.headText.textContent = txt;
|
||||
} */
|
||||
}
|
||||
}
|
||||
|
||||
private lastFlashTime: number = 0;
|
||||
@@ -1282,46 +1486,77 @@ namespace pxsim.visuals {
|
||||
|
||||
private updateLightLevel() {
|
||||
let state = this.board;
|
||||
const valMin = 0;
|
||||
const valMax = 255;
|
||||
if (!state || !state.lightSensorState.usesLightLevel) return;
|
||||
|
||||
if (!this.lightLevelButton) {
|
||||
let gid = "gradient-light-level";
|
||||
this.lightLevelGradient = svg.linearGradient(this.defs, gid)
|
||||
const cx = 30;
|
||||
const cy = 45;
|
||||
const r = 20;
|
||||
const cx = 25;
|
||||
const cy = 75;
|
||||
const r = 55;
|
||||
|
||||
this.lightLevelButton = svg.child(this.g, "circle", {
|
||||
cx: `${cx}px`, cy: `${cy}px`, r: `${r}px`,
|
||||
class: 'sim-light-level-button',
|
||||
fill: `url(#${gid})`
|
||||
}) as SVGCircleElement;
|
||||
let pt = this.element.createSVGPoint();
|
||||
|
||||
svg.buttonEvents(this.lightLevelButton,
|
||||
// move
|
||||
(ev) => {
|
||||
let pos = svg.cursorPoint(pt, this.element, ev);
|
||||
let rs = r / 2;
|
||||
let level = Math.max(0, Math.min(255, Math.floor((pos.y - (cy - rs)) / (2 * rs) * 255)));
|
||||
if (level != this.board.lightSensorState.lightLevel) {
|
||||
this.board.lightSensorState.lightLevel = level;
|
||||
let level = valMax - Math.max(valMin, Math.min(valMax, Math.floor((pos.y - (cy - r)) / (2 * r) * valMax)));
|
||||
|
||||
if (level != state.lightSensorState.lightLevel) {
|
||||
state.lightSensorState.lightLevel = level;
|
||||
this.applyLightLevel();
|
||||
}
|
||||
}, ev => { },
|
||||
ev => { })
|
||||
this.lightLevelText = svg.child(this.g, "text", { x: cx - r - 7, y: cy + r + 8, text: '', class: 'sim-text inverted' }) as SVGTextElement;
|
||||
},
|
||||
// start
|
||||
ev => { },
|
||||
// stop
|
||||
ev => { },
|
||||
// keydown
|
||||
ev => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
state.lightSensorState.lightLevel--;
|
||||
if(state.lightSensorState.lightLevel < valMin) state.lightSensorState.lightLevel = valMin;
|
||||
this.applyLightLevel();
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
state.lightSensorState.lightLevel++
|
||||
if(state.lightSensorState.lightLevel > valMax) state.lightSensorState.lightLevel = valMax;
|
||||
this.applyLightLevel();
|
||||
}
|
||||
})
|
||||
this.lightLevelText = svg.child(this.g, "text", { x: cx , y: cy + r + 35, text: '', class: 'sim-text inverted big centered' }) as SVGTextElement;
|
||||
this.updateTheme();
|
||||
}
|
||||
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor(state.lightSensorState.lightLevel * 100 / 255))) + '%')
|
||||
accessibility.makeFocusable(this.lightLevelButton);
|
||||
accessibility.setAria(this.lightLevelButton, "slider", pxsim.localization.lf("Light Level"));
|
||||
this.lightLevelButton.setAttribute("aria-valuemin", valMin + "");
|
||||
this.lightLevelButton.setAttribute("aria-valuemax", valMax + "");
|
||||
this.lightLevelButton.setAttribute("aria-orientation", "vertical");
|
||||
this.lightLevelButton.setAttribute("aria-valuenow", state.lightSensorState.lightLevel + "");
|
||||
this.lightLevelButton.setAttribute("aria-valuetext", state.lightSensorState.lightLevel + "");
|
||||
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor((255 - state.lightSensorState.lightLevel) * 100 / 255))) + '%')
|
||||
this.lightLevelText.textContent = state.lightSensorState.lightLevel.toString();
|
||||
}
|
||||
|
||||
private applyLightLevel() {
|
||||
let lv = this.board.lightSensorState.lightLevel;
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor(lv * 100 / 255))) + '%')
|
||||
svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor((255 - lv) * 100 / 255))) + '%')
|
||||
this.lightLevelText.textContent = lv.toString();
|
||||
}
|
||||
|
||||
private updateTilt() {
|
||||
return;
|
||||
if (this.props.disableTilt) return;
|
||||
let state = this.board;
|
||||
if (!state || !state.accelerometerState.accelerometer.isActive) return;
|
||||
@@ -1347,6 +1582,7 @@ namespace pxsim.visuals {
|
||||
"y": "0px",
|
||||
"width": MB_WIDTH + "px",
|
||||
"height": MB_HEIGHT + "px",
|
||||
"fill": "rgba(0,0,0,0)"
|
||||
});
|
||||
this.style = <SVGStyleElement>svg.child(this.element, "style", {});
|
||||
this.style.textContent = MB_STYLE;
|
||||
@@ -1356,6 +1592,15 @@ namespace pxsim.visuals {
|
||||
this.element.appendChild(this.g);
|
||||
|
||||
// filters
|
||||
let ledglow = svg.child(this.defs, "filter", { id: "ledglow", x: "-75%", y: "-75%", width: "300%", height: "300%" });
|
||||
svg.child(ledglow, "feMorphology", { operator: "dilate", radius: "4", in: "SourceAlpha", result: "thicken" });
|
||||
svg.child(ledglow, "feGaussianBlur", { stdDeviation: "5", in: "thicken", result: "blurred" });
|
||||
svg.child(ledglow, "feFlood", { "flood-color": "rgb(255, 17, 77)", result: "glowColor" });
|
||||
svg.child(ledglow, "feComposite", { in: "glowColor", in2: "blurred", operator: "in", result: "ledglow_colored" });
|
||||
let ledglowMerge = svg.child(ledglow, "feMerge", {});
|
||||
svg.child(ledglowMerge, "feMergeNode", { in: "ledglow_colored" });
|
||||
svg.child(ledglowMerge, "feMergeNode", { in: "SourceGraphic" });
|
||||
|
||||
let glow = svg.child(this.defs, "filter", { id: "filterglow", x: "-5%", y: "-5%", width: "120%", height: "120%" });
|
||||
svg.child(glow, "feGaussianBlur", { stdDeviation: "5", result: "glow" });
|
||||
let merge = svg.child(glow, "feMerge", {});
|
||||
@@ -1368,24 +1613,42 @@ namespace pxsim.visuals {
|
||||
const top = Number(this.element.getElementById("LED_0_0").getAttribute("y"));
|
||||
const ledoffw = Number(this.element.getElementById("LED_1_0").getAttribute("x"))-left;
|
||||
const ledoffh = Number(this.element.getElementById("LED_0_1").getAttribute("y"))-top;
|
||||
const ledw = 5.1;
|
||||
const ledh = 12.9;
|
||||
// const ledw = 5.1;
|
||||
// const ledh = 12.9;
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
let ledtop = i * ledoffh + top;
|
||||
for (let j = 0; j < 5; ++j) {
|
||||
let ledleft = j * ledoffw + left;
|
||||
let k = i * 5 + j;
|
||||
this.ledsOuter.push(svg.child(this.g, "rect", { class: "sim-led-back", x: ledleft, y: ledtop, width: ledw, height: ledh }));
|
||||
this.leds.push(svg.child(this.g, "rect", { class: "sim-led", x: ledleft - 1, y: ledtop - 1, width: ledw + 2, height: ledh + 2, rx: 2, ry: 2, title: `(${j},${i})` }));
|
||||
this.ledsOuter.push(svg.child(this.g, "rect", { class: "sim-led-back", x: ledleft, y: ledtop, width: 10, height: 20, rx: 2, ry: 2 }));
|
||||
let led = svg.child(this.g, "rect", { class: "sim-led", x: ledleft - 2, y: ledtop - 2, width: 14, height: 24, rx: 3, ry: 3, title: `(${j},${i})` });
|
||||
svg.filter(led, `url(#ledglow)`)
|
||||
this.leds.push(led);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// head
|
||||
// this.headg = <SVGGElement>svg.child(this.g, "g", { style: "transform: translate(100px, 0px);" });
|
||||
this.head = <SVGGElement>svg.child(this.g, "g", { class: "sim-head" });
|
||||
svg.child(this.head, "circle", { cx: 501.2, cy: 75, r: 100, fill: "transparent" })
|
||||
this.headParts = <SVGGElement>svg.child(this.head, "g", { class: "sim-button-outer sim-button-group" });
|
||||
this.heads = []
|
||||
// background
|
||||
this.heads.push(svg.path(this.headParts, "sim-button",""));
|
||||
// shapes
|
||||
this.heads.push(<SVGGElement>svg.child(this.headParts, "g", { class: "sim-theme" }));
|
||||
// this.heads.push(svg.path(this.headParts, "sim-theme", "M230.6,69.7c-2.9,0-5.3,2.4-5.3,5.3c0,2.9,2.4,5.3,5.3,5.3c2.9,0,5.3-2.4,5.3-5.3C235.9,72.1,233.5,69.7,230.6,69.7"));
|
||||
// this.heads.push(svg.path(this.headParts, "sim-theme", "M269.7,80.3c2.9,0,5.3-2.4,5.3-5.3c0-2.9-2.4-5.3-5.3-5.3c-2.9,0-5.3,2.4-5.3,5.3C264.4,77.9,266.8,80.3,269.7,80.3"));
|
||||
this.headText = <SVGTextElement>svg.child(this.g, "text", { x: 500, y: 165, class: "sim-text inverted big centered" })
|
||||
|
||||
// https://www.microbit.co.uk/device/pins
|
||||
// P0, P1, P2
|
||||
this.pins = pinNames.map(n => {
|
||||
let p = this.element.getElementById(n) as SVGElement;
|
||||
if(!p) console.log("missing "+n);
|
||||
U.addClass(p, "sim-pin");
|
||||
// console.log(p);
|
||||
return p;
|
||||
});
|
||||
|
||||
@@ -1398,11 +1661,18 @@ namespace pxsim.visuals {
|
||||
return lg;
|
||||
})
|
||||
|
||||
// this.pinTexts = [
|
||||
// [-20, 340],
|
||||
// [50, 495],
|
||||
// [450, 495],
|
||||
// [500, 340]
|
||||
// ].map(p => <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: p[0], y: p[1] }));
|
||||
|
||||
this.pinTexts = {
|
||||
[DigitalPin.P0]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: -20, y: 340 }),
|
||||
[DigitalPin.P1]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: 50, y: 495 }),
|
||||
[DigitalPin.P2]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: 450, y: 495 }),
|
||||
[DigitalPin.P3]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin", x: 500, y: 340 })
|
||||
[DigitalPin.P0]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 20, y: 315 }),
|
||||
[DigitalPin.P1]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 135, y: 530 }),
|
||||
[DigitalPin.P2]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 395, y: 530 }),
|
||||
[DigitalPin.P3]: <SVGTextElement>svg.child(this.g, "text", { class: "sim-text-pin big centered", x: 535, y: 315 })
|
||||
}
|
||||
|
||||
// BTN A, B
|
||||
@@ -1421,8 +1691,8 @@ namespace pxsim.visuals {
|
||||
return button;
|
||||
}
|
||||
|
||||
let ab = outerBtn(69, MB_HEIGHT - 45);
|
||||
let abtext = svg.child(ab.outer, "text", { x: 67, y: MB_HEIGHT - 10, class: "sim-text inverted" }) as SVGTextElement;
|
||||
let ab = outerBtn(210, MB_HEIGHT - 45);
|
||||
let abtext = svg.child(ab.outer, "text", { x: 210, y: MB_HEIGHT - 5, class: "sim-text big inverted centered" }) as SVGTextElement;
|
||||
abtext.textContent = "A+B";
|
||||
(<any>this.buttonsOuter[2]).style.visibility = "hidden";
|
||||
(<any>this.buttons[2]).style.visibility = "hidden";
|
||||
@@ -1453,15 +1723,31 @@ namespace pxsim.visuals {
|
||||
}
|
||||
|
||||
private attachEvents() {
|
||||
this.attachIFrameEvents();
|
||||
this.attachAccelerometerEvents();
|
||||
this.attachPinsIOEvents();
|
||||
this.attachPinsTouchEvents();
|
||||
this.attachABEvents();
|
||||
this.attachAPlusBEvents();
|
||||
}
|
||||
|
||||
private attachIFrameEvents() {
|
||||
Runtime.messagePosted = (msg) => {
|
||||
switch (msg.type || "") {
|
||||
case "serial": this.flashSystemLed(); break;
|
||||
case "radiopacket": this.flashAntenna(); break;
|
||||
case "eventbus":
|
||||
if ((<pxsim.SimulatorEventBusMessage>msg).id == DAL.MES_BROADCAST_GENERAL_ID)
|
||||
this.flashAntenna();
|
||||
break;
|
||||
}
|
||||
}
|
||||
let tiltDecayer = 0;
|
||||
}
|
||||
|
||||
private attachAccelerometerEvents() {
|
||||
let tiltDecayer: any = undefined;
|
||||
this.element.addEventListener(pointerEvents.move, (ev: MouseEvent) => {
|
||||
let state = this.board;
|
||||
const state = this.board;
|
||||
if (!state.accelerometerState.accelerometer.isActive) return;
|
||||
|
||||
if (tiltDecayer) {
|
||||
@@ -1469,14 +1755,19 @@ namespace pxsim.visuals {
|
||||
tiltDecayer = 0;
|
||||
}
|
||||
|
||||
let bbox = this.element.getBoundingClientRect();
|
||||
let ax = (ev.clientX - bbox.width / 2) / (bbox.width / 3);
|
||||
let ay = (ev.clientY - bbox.height / 2) / (bbox.height / 3);
|
||||
const bbox = this.element.getBoundingClientRect();
|
||||
|
||||
let x = - Math.max(- 1023, Math.min(1023, Math.floor(ax * 1023)));
|
||||
let y = - Math.max(- 1023, Math.min(1023, Math.floor(ay * 1023)));
|
||||
let z2 = 1023 * 1023 - x * x - y * y;
|
||||
let z = Math.floor((z2 > 0 ? -1 : 1) * Math.sqrt(Math.abs(z2)));
|
||||
// ev.clientX and ev.clientY are not defined on mobile iOS
|
||||
const xPos = ev.clientX != null ? ev.clientX : ev.pageX;
|
||||
const yPos = ev.clientY != null ? ev.clientY : ev.pageY;
|
||||
|
||||
const ax = (xPos - bbox.width / 2) / (bbox.width / 3);
|
||||
const ay = (yPos - bbox.height / 2) / (bbox.height / 3);
|
||||
|
||||
const x = - Math.max(- 1023, Math.min(1023, Math.floor(ax * 1023)));
|
||||
const y = - Math.max(- 1023, Math.min(1023, Math.floor(ay * 1023)));
|
||||
const z2 = 1023 * 1023 - x * x - y * y;
|
||||
const z = Math.floor((z2 > 0 ? -1 : 1) * Math.sqrt(Math.abs(z2)));
|
||||
|
||||
state.accelerometerState.accelerometer.update(x, y, z);
|
||||
this.updateTilt();
|
||||
@@ -1504,10 +1795,15 @@ namespace pxsim.visuals {
|
||||
}, 50)
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
this.pins.forEach((pin, index) => {
|
||||
private attachPinsIOEvents() {
|
||||
this.pins.slice(2, 6).forEach((pin, index) => {
|
||||
// var index = i + 2;
|
||||
if (!this.board.edgeConnectorState.pins[index]) return;
|
||||
let pt = this.element.createSVGPoint();
|
||||
let xpos = (index === 0 || index === 3) ? 300 : 520;
|
||||
let vMax = (index === 0 || index === 3) ? 1 : 1032;
|
||||
svg.buttonEvents(pin,
|
||||
// move
|
||||
ev => {
|
||||
@@ -1516,8 +1812,8 @@ namespace pxsim.visuals {
|
||||
let svgpin = this.pins[index];
|
||||
if (pin.mode & PinFlags.Input) {
|
||||
let cursor = svg.cursorPoint(pt, this.element, ev);
|
||||
let v = (400 - cursor.y) / 40 * 1023
|
||||
pin.value = Math.max(0, Math.min(1023, Math.floor(v)));
|
||||
let v = (xpos - cursor.y) / 70 * (vMax + 1);
|
||||
pin.value = Math.max(0, Math.min(vMax, Math.floor(v)));
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
},
|
||||
@@ -1529,8 +1825,8 @@ namespace pxsim.visuals {
|
||||
U.addClass(svgpin, "touched");
|
||||
if (pin.mode & PinFlags.Input) {
|
||||
let cursor = svg.cursorPoint(pt, this.element, ev);
|
||||
let v = (400 - cursor.y) / 40 * 1023
|
||||
pin.value = Math.max(0, Math.min(1023, Math.floor(v)));
|
||||
let v = (xpos - cursor.y) / 70 * (vMax + 1);
|
||||
pin.value = Math.max(0, Math.min(vMax, Math.floor(v)));
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
},
|
||||
@@ -1542,82 +1838,154 @@ namespace pxsim.visuals {
|
||||
U.removeClass(svgpin, "touched");
|
||||
this.updatePin(pin, index);
|
||||
return false;
|
||||
},
|
||||
// keydown
|
||||
(ev: KeyboardEvent) => {
|
||||
let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode
|
||||
let state = this.board;
|
||||
let pin = state.edgeConnectorState.pins[index];
|
||||
|
||||
if (charCode === 40 || charCode === 37) { // Down/Left arrow
|
||||
pin.value -= 10;
|
||||
if (pin.value < 0) {
|
||||
pin.value = 1023;
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
} else if (charCode === 38 || charCode === 39) { // Up/Right arrow
|
||||
pin.value += 10;
|
||||
if (pin.value > 1023) {
|
||||
pin.value = 0;
|
||||
}
|
||||
this.updatePin(pin, index);
|
||||
}
|
||||
});
|
||||
})
|
||||
this.pins.slice(0, 2).forEach((btn, index) => {
|
||||
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
state.edgeConnectorState.pins[index].touched = true;
|
||||
this.updatePin(state.edgeConnectorState.pins[index], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
state.edgeConnectorState.pins[index].touched = false;
|
||||
this.updatePin(state.edgeConnectorState.pins[index], index);
|
||||
})
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
let state = this.board;
|
||||
state.edgeConnectorState.pins[index].touched = false;
|
||||
this.updatePin(state.edgeConnectorState.pins[index], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let bpState = this.board.buttonPairState;
|
||||
let stateButtons = [bpState.aBtn, bpState.bBtn, bpState.abBtn];
|
||||
this.buttonsOuter.slice(0, 2).forEach((btn, index) => {
|
||||
private attachPinsTouchEvents() {
|
||||
this.pins.slice(2, 6).forEach((btn, i) => {
|
||||
var index = i + 2;
|
||||
let state = this.board;
|
||||
let pressedTime: number;
|
||||
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[index].pressed = true;
|
||||
svg.fill(this.buttons[index], this.props.theme.buttonDown);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
// console.log(`down ${state.edgeConnectorState.pins[i].id}`)
|
||||
state.edgeConnectorState.pins[i].touched = true;
|
||||
this.updatePin(state.edgeConnectorState.pins[i], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
pressedTime = runtime.runningTime()
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[index].pressed = false;
|
||||
svg.fill(this.buttons[index], this.props.theme.buttonUps[index]);
|
||||
})
|
||||
// btn.addEventListener(pointerEvents.leave, ev => {
|
||||
// let state = this.board;
|
||||
// state.edgeConnectorState.pins[i].touched = false;
|
||||
// this.updatePin(state.edgeConnectorState.pins[i], index);
|
||||
// })
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
let state = this.board;
|
||||
// console.log(`up ${state.edgeConnectorState.pins[i].id}, index ${index}`)
|
||||
state.edgeConnectorState.pins[i].touched = false;
|
||||
this.updatePin(state.edgeConnectorState.pins[i], index);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
const currentTime = runtime.runningTime()
|
||||
if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME) {
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK);
|
||||
// console.log(`& long click`)
|
||||
} else {
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
// console.log(`& click`)
|
||||
}
|
||||
pressedTime = undefined;
|
||||
})
|
||||
accessibility.enableKeyboardInteraction(btn, undefined, () => {
|
||||
let state = this.board;
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(state.edgeConnectorState.pins[i].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
private attachABEvents() {
|
||||
const bpState = this.board.buttonPairState;
|
||||
const stateButtons: Button[] = [bpState.aBtn, bpState.bBtn];
|
||||
const elButtonOuters = this.buttonsOuter.slice(0, 2);
|
||||
const elButtons = this.buttons.slice(0, 2);
|
||||
|
||||
elButtonOuters.forEach((btn, index) => {
|
||||
let pressedTime: number;
|
||||
pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => {
|
||||
// console.log(`down ${stateButtons[index].id}`)
|
||||
stateButtons[index].pressed = true;
|
||||
svg.fill(elButtons[index], this.props.theme.buttonDown);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
pressedTime = runtime.runningTime()
|
||||
}));
|
||||
btn.addEventListener(pointerEvents.leave, ev => {
|
||||
stateButtons[index].pressed = false;
|
||||
svg.fill(this.buttons[index], this.props.theme.buttonUps[index]);
|
||||
svg.fill(elButtons[index], this.props.theme.buttonUps[0]);
|
||||
})
|
||||
btn.addEventListener(pointerEvents.up, ev => {
|
||||
stateButtons[index].pressed = false;
|
||||
svg.fill(elButtons[index], this.props.theme.buttonUps[0]);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
const currentTime = runtime.runningTime()
|
||||
if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME)
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK);
|
||||
else
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
pressedTime = undefined;
|
||||
})
|
||||
accessibility.enableKeyboardInteraction(btn, undefined, () => {
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
private attachAPlusBEvents() {
|
||||
const bpState = this.board.buttonPairState;
|
||||
let pressedTime: number;
|
||||
// A+B
|
||||
pointerEvents.down.forEach(evid => this.buttonsOuter[2].addEventListener(evid, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = true;
|
||||
stateButtons[1].pressed = true;
|
||||
stateButtons[2].pressed = true;
|
||||
bpState.aBtn.pressed = true;
|
||||
bpState.bBtn.pressed = true;
|
||||
bpState.abBtn.pressed = true;
|
||||
svg.fill(this.buttons[0], this.props.theme.buttonDown);
|
||||
svg.fill(this.buttons[1], this.props.theme.buttonDown);
|
||||
svg.fill(this.buttons[2], this.props.theme.buttonDown);
|
||||
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
pressedTime = runtime.runningTime()
|
||||
}));
|
||||
this.buttonsOuter[2].addEventListener(pointerEvents.leave, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = false;
|
||||
stateButtons[1].pressed = false;
|
||||
stateButtons[2].pressed = false;
|
||||
bpState.aBtn.pressed = false;
|
||||
bpState.bBtn.pressed = false;
|
||||
bpState.abBtn.pressed = false;
|
||||
svg.fill(this.buttons[0], this.props.theme.buttonUps[0]);
|
||||
svg.fill(this.buttons[1], this.props.theme.buttonUps[1]);
|
||||
svg.fill(this.buttons[2], this.props.theme.virtualButtonUp);
|
||||
})
|
||||
this.buttonsOuter[2].addEventListener(pointerEvents.up, ev => {
|
||||
let state = this.board;
|
||||
stateButtons[0].pressed = false;
|
||||
stateButtons[1].pressed = false;
|
||||
stateButtons[2].pressed = false;
|
||||
bpState.aBtn.pressed = false;
|
||||
bpState.bBtn.pressed = false;
|
||||
bpState.abBtn.pressed = false;
|
||||
svg.fill(this.buttons[0], this.props.theme.buttonUps[0]);
|
||||
svg.fill(this.buttons[1], this.props.theme.buttonUps[1]);
|
||||
svg.fill(this.buttons[2], this.props.theme.virtualButtonUp);
|
||||
|
||||
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(stateButtons[2].id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
const currentTime = runtime.runningTime()
|
||||
if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME)
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK);
|
||||
else
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
pressedTime = undefined;
|
||||
})
|
||||
accessibility.enableKeyboardInteraction(this.buttonsOuter[2], undefined, () => {
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP);
|
||||
this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user