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

Commit 377bc3e

Browse files
authored
Switch slider support (#25)
[PBI #29091] * Adding switch svg interaction * Moving the switch click logic out to simulator.tsx * Adding state handling for the switch on React side * Adding the switch state handling on the Python side and API method * Removing comments in Cpx.txs * Using prpos.switch for the switch state check in Cpx.tsx * Correcting the name of HOVER attribute in the css class to allow on hover color change * Adding the correct pressed attribute for click on the switch * Correcting errors printing
1 parent 6dde19f commit 377bc3e

File tree

8 files changed

+136
-56
lines changed

8 files changed

+136
-56
lines changed

src/adafruit_circuitplayground/express.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from . import utils
77

88

9-
109
class Express:
1110
def __init__(self):
1211
# State in the Python process
@@ -26,7 +25,8 @@ def __init__(self):
2625
(0, 0, 0),
2726
(0, 0, 0)
2827
],
29-
'red_led': False
28+
'red_led': False,
29+
'switch': False
3030
}
3131

3232
self.pixels = Pixel(self.__state)
@@ -50,6 +50,10 @@ def red_led(self, value):
5050
self.__state['red_led'] = bool(value)
5151
self.__show()
5252

53+
@property
54+
def switch(self):
55+
return self.__state['switch']
56+
5357
def __show(self):
5458
utils.show(self.__state)
5559

@@ -71,5 +75,5 @@ def play_file(self, file_name):
7175
raise TypeError(file_name + " is not a path to a .wav file.")
7276
else:
7377
raise NotImplementedError("Please use Python 3 or higher.")
74-
78+
7579
cpx = Express()

src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const localize: nls.LocalizeFunc = nls.config({
77
export const CONSTANTS = {
88
ERROR: {
99
STDERR: (data: string) => {
10-
return localize("error.stderr", "[ERROR] {0} \n", data);
10+
return localize("error.stderr", `[ERROR] ${data} \n`);
1111
},
1212
UNEXPECTED_MESSAGE: localize(
1313
"error.unexpectedMessage",

src/setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def run(self):
2525
'button_a', cpx._Express__state['button_a'])
2626
cpx._Express__state['button_b'] = new_state.get(
2727
'button_b', cpx._Express__state['button_b'])
28+
cpx._Express__state['switch'] = new_state.get(
29+
'switch', cpx._Express__state['switch'])
2830
except Exception as e:
2931
print("Error trying to send event to the process : ",
3032
e, file=sys.stderr, flush=True)

src/view/components/Simulator.tsx

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import * as React from "react";
22
import { BUTTON_NEUTRAL, BUTTON_PRESSED } from "./cpx/Cpx_svg_style";
33
import Cpx from "./cpx/Cpx";
4+
import svg from "./cpx/Svg_utils";
45

56
interface IState {
67
pixels: Array<Array<number>>;
78
brightness: number;
89
red_led: boolean;
9-
button_a: any;
10-
button_b: any;
10+
button_a: boolean;
11+
button_b: boolean;
12+
switch: boolean;
1113
}
1214
interface IMyProps {
1315
children?: any;
@@ -29,8 +31,8 @@ const DEFAULT_STATE: IState = {
2931
[0, 0, 0],
3032
[0, 0, 0]
3133
],
32-
33-
red_led: false
34+
red_led: false,
35+
switch: false
3436
};
3537

3638
interface vscode {
@@ -89,6 +91,7 @@ class Simulator extends React.Component<any, IState> {
8991
pixels={this.state.pixels}
9092
brightness={this.state.brightness}
9193
red_led={this.state.red_led}
94+
switch={this.state.switch}
9295
onMouseUp={this.onMouseUp}
9396
onMouseDown={this.onMouseDown}
9497
onMouseLeave={this.onMouseLeave}
@@ -115,6 +118,17 @@ class Simulator extends React.Component<any, IState> {
115118
}
116119

117120
private handleClick(button: HTMLElement, active: boolean) {
121+
let newState = undefined;
122+
if (button.id.includes("BTN")) {
123+
newState = this.handleButtonClick(button, active);
124+
} else if (button.id.includes("SWITCH")) {
125+
newState = this.handleSwitchClick(button);
126+
} else return;
127+
128+
if (newState) sendMessage(newState);
129+
}
130+
131+
private handleButtonClick(button: HTMLElement, active: boolean) {
118132
const ButtonA: boolean = button.id.match(/BTN_A/) !== null;
119133
const ButtonB: boolean = button.id.match(/BTN_B/) !== null;
120134
const ButtonAB: boolean = button.id.match(/BTN_AB/) !== null;
@@ -140,16 +154,37 @@ class Simulator extends React.Component<any, IState> {
140154
};
141155
this.setState(newState);
142156
}
143-
button.setAttribute("pressed", `${active}`);
144-
if (newState) sendMessage(newState);
145157
if (innerButton) innerButton.style.fill = this.getButtonColor(active);
158+
button.setAttribute("pressed", `${active}`);
159+
return newState;
146160
}
147161

148162
private getButtonColor(pressed: boolean) {
149163
const buttonUps = BUTTON_NEUTRAL;
150164
const buttonDown = BUTTON_PRESSED;
151165
return pressed ? buttonDown : buttonUps;
152166
}
167+
168+
private handleSwitchClick(button: HTMLElement) {
169+
const switchInner = (window.document.getElementById(
170+
"SWITCH_INNER"
171+
) as unknown) as SVGElement;
172+
173+
svg.addClass(switchInner, "sim-slide-switch-inner");
174+
175+
let switchIsOn: boolean = !this.state.switch;
176+
177+
if (switchIsOn) {
178+
svg.addClass(switchInner, "on");
179+
switchInner.setAttribute("transform", "translate(-5,0)");
180+
} else {
181+
svg.removeClass(switchInner, "on");
182+
switchInner.removeAttribute("transform");
183+
}
184+
this.setState({ switch: switchIsOn });
185+
button.setAttribute("aria-pressed", switchIsOn.toString());
186+
return { switch: switchIsOn };
187+
}
153188
}
154189

155190
export default Simulator;

src/view/components/cpx/Cpx.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface IProps {
88
pixels: Array<Array<number>>;
99
red_led: boolean;
1010
brightness: number;
11+
switch: boolean;
1112
onMouseUp: (button: HTMLElement, event: Event) => void;
1213
onMouseDown: (button: HTMLElement, event: Event) => void;
1314
onMouseLeave: (button: HTMLElement, event: Event) => void;
@@ -23,12 +24,12 @@ const Cpx: React.FC<IProps> = props => {
2324
if (firstTime) {
2425
initSvgStyle(svgElement, props.brightness);
2526
setupButtons(props);
27+
setupSwitch(props);
2628
firstTime = false;
2729
}
28-
// Update Neopixels state
30+
// Update Neopixels and red LED state
2931
updateNeopixels(props);
3032
updateRedLED(props.red_led);
31-
setupButtons(props);
3233
}
3334

3435
return CPX_SVG;
@@ -261,4 +262,29 @@ const setupButton = (button: HTMLElement, className: string, props: IProps) => {
261262
svgButton.onmouseleave = e => props.onMouseLeave(button, e);
262263
};
263264

265+
const setupSwitch = (props: IProps): void => {
266+
const switchElement = window.document.getElementById("SWITCH");
267+
const swInnerElement = window.document.getElementById("SWITCH_INNER");
268+
const swHousingElement = window.document.getElementById("SWITCH_HOUSING");
269+
270+
if (switchElement && swInnerElement && swHousingElement) {
271+
let svgSwitch: SVGElement = (switchElement as unknown) as SVGElement;
272+
let svgSwitchInner: SVGElement = (swInnerElement as unknown) as SVGElement;
273+
let svgSwitchHousing: SVGElement = (swHousingElement as unknown) as SVGElement;
274+
275+
svg.addClass(svgSwitch, "sim-slide-switch");
276+
277+
svgSwitch.onmouseup = e => props.onMouseUp(switchElement, e);
278+
svgSwitchInner.onmouseup = e => props.onMouseUp(swInnerElement, e);
279+
svgSwitchHousing.onmouseup = e => props.onMouseUp(swHousingElement, e);
280+
281+
accessibility.makeFocusable(svgSwitch);
282+
accessibility.setAria(
283+
svgSwitch,
284+
"button",
285+
"On/Off Switch. Current state : " + props.switch ? "On" : "Off"
286+
);
287+
}
288+
};
289+
264290
export default Cpx;

src/view/components/cpx/Cpx_svg.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2706,17 +2706,17 @@ export const CPX_SVG = (
27062706
id="path2486"
27072707
fill="#b4b4b4"
27082708
/>
2709-
<path d="M62.138 51.542h19.8v-9.899h-19.8v9.899z" id="SLIDE_HOUSING" />
2710-
<g id="SLIDE" transform="matrix(1 0 0 -1 80.538 51.542)">
2709+
<path d="M62.138 51.542h19.8v-9.899h-19.8v9.899z" id="SWITCH_HOUSING" />
2710+
<g id="SWITCH" transform="matrix(1 0 0 -1 80.538 51.542)">
27112711
<path
27122712
d="M0 0v2.9h1.4v4.201H0V10h-4.2V7.101h-8.5V10H-17V7.101h-1.4V2.9h1.4V0H0z"
2713-
id="SLIDE_HOVER"
2713+
id="SWITCH_HOVER"
27142714
fill="#dcdcdc"
27152715
/>
27162716
</g>
27172717
<path
27182718
d="M72.038 44.342h4.3v-2.8h-4.3v2.8z"
2719-
id="SLIDE_INNER"
2719+
id="SWITCH_INNER"
27202720
fill="#333"
27212721
/>
27222722
<g id="g2496" transform="translate(69.938 47.943)">

src/view/components/cpx/Cpx_svg_style.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,10 @@ export const SVG_STYLE = `
162162
r:8px;
163163
}
164164
165-
#SLIDE_HOVER {
165+
#SWITCH_HOVER {
166166
cursor: pointer;
167167
}
168-
.sim-slide-switch:hover #SLIDE_HOVER {
168+
.sim-slide-switch:hover #SWITCH_HOVER {
169169
stroke:orange !important;
170170
stroke-width: 1px;
171171
}

src/view/components/cpx/Svg_utils.tsx

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,64 @@ namespace svg {
77
el.className.baseVal += " " + cls;
88
}
99

10-
export function hydrate(el: SVGElement, props: any) {
11-
for (let k in props) {
12-
if (k == "title") {
13-
svg.title(el, props[k])
14-
} else el.setAttributeNS(null, k, props[k])
15-
}
16-
}
10+
export function removeClass(el: SVGElement, cls: string) {
11+
if (el.classList) el.classList.remove(cls);
12+
else
13+
el.className.baseVal = el.className.baseVal
14+
.replace(cls, "")
15+
.replace(/\s{2,}/, " ");
16+
}
1717

18-
export function createElement(name: string, props?: any): SVGElement {
19-
let newElement = document.createElementNS("http://www.w3.org/2000/svg", name)
20-
if (props)
21-
svg.hydrate(newElement, props);
22-
return newElement;
18+
export function hydrate(el: SVGElement, props: any) {
19+
for (let k in props) {
20+
if (k == "title") {
21+
svg.title(el, props[k]);
22+
} else el.setAttributeNS(null, k, props[k]);
2323
}
24+
}
2425

25-
export function child(parent: Element, name: string, props?: any): SVGElement {
26-
let childElement = svg.createElement(name, props);
27-
parent.appendChild(childElement);
28-
return childElement;
29-
}
26+
export function createElement(name: string, props?: any): SVGElement {
27+
let newElement = document.createElementNS(
28+
"http://www.w3.org/2000/svg",
29+
name
30+
);
31+
if (props) svg.hydrate(newElement, props);
32+
return newElement;
33+
}
3034

31-
export function fill(el: SVGElement, c: string) {
32-
el.style.fill = c;
33-
}
35+
export function child(
36+
parent: Element,
37+
name: string,
38+
props?: any
39+
): SVGElement {
40+
let childElement = svg.createElement(name, props);
41+
parent.appendChild(childElement);
42+
return childElement;
43+
}
3444

35-
export function filter(el: SVGElement, c: string) {
36-
el.style.filter = c;
37-
}
45+
export function fill(el: SVGElement, c: string) {
46+
el.style.fill = c;
47+
}
3848

39-
export function fills(els: SVGElement[], c: string) {
40-
els.forEach(el => el.style.fill = c);
41-
}
49+
export function filter(el: SVGElement, c: string) {
50+
el.style.filter = c;
51+
}
4252

43-
export function mkTitle(txt: string): SVGTitleElement {
44-
let t = svg.createElement("title") as SVGTitleElement;
45-
t.textContent = txt;
46-
return t;
47-
}
53+
export function fills(els: SVGElement[], c: string) {
54+
els.forEach(el => (el.style.fill = c));
55+
}
4856

49-
export function title(el: SVGElement, txt: string): SVGTitleElement {
50-
let t = mkTitle(txt);
51-
el.appendChild(t);
52-
return t;
53-
}
54-
}
57+
export function mkTitle(txt: string): SVGTitleElement {
58+
let t = svg.createElement("title") as SVGTitleElement;
59+
t.textContent = txt;
60+
return t;
61+
}
5562

63+
export function title(el: SVGElement, txt: string): SVGTitleElement {
64+
let t = mkTitle(txt);
65+
el.appendChild(t);
66+
return t;
67+
}
68+
}
5669

57-
export default svg;
70+
export default svg;

0 commit comments

Comments
 (0)