Update simulator as per latest lego UI design for sensors. Fix full screen layout issues. (#554)

This commit is contained in:
Sam El-Husseini 2018-05-02 13:58:59 -07:00 committed by GitHub
parent 4825172423
commit 84990d66a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 376 additions and 207 deletions

View File

@ -49,14 +49,14 @@ namespace pxsim.visuals {
font-size:16px; font-size:16px;
} }
.sim-text.large { .sim-text.large {
font-size:30px; font-size:20px;
} }
.sim-text.number { .sim-text.number {
font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro; font-family: Courier, Lato, Work Sans, PT Serif, Source Serif Pro;
/*font-weight: bold;*/ /*font-weight: bold;*/
} }
.sim-text.inverted { .sim-text.inverted {
fill:#5A5A5A; fill: #5A5A5A; /*#F12B21;*/
} }
.no-drag, .sim-text, .sim-text-pin { .no-drag, .sim-text, .sim-text-pin {
@ -74,6 +74,10 @@ namespace pxsim.visuals {
stroke: #000; stroke: #000;
cursor: pointer; cursor: pointer;
} }
.sim-color-selected {
stroke-width: 1px !important;
stroke: #A8AAA8 !important;
}
.sim-color-wheel-half:hover { .sim-color-wheel-half:hover {
stroke-width: 1; stroke-width: 1;
stroke: #000; stroke: #000;
@ -136,6 +140,8 @@ namespace pxsim.visuals {
private cachedControlNodes: { [index: string]: View[] } = {}; private cachedControlNodes: { [index: string]: View[] } = {};
private cachedDisplayViews: { [index: string]: LayoutElement[] } = {}; private cachedDisplayViews: { [index: string]: LayoutElement[] } = {};
private cachedCloseIcons: { [index: string]: View } = {};
private cachedBackgroundViews: { [index: string]: View } = {};
private screenCanvas: HTMLCanvasElement; private screenCanvas: HTMLCanvasElement;
private screenCanvasCtx: CanvasRenderingContext2D; private screenCanvasCtx: CanvasRenderingContext2D;
@ -298,8 +304,22 @@ namespace pxsim.visuals {
return undefined; return undefined;
} }
private getCloseIconView() { private getCloseIconView(port: number) {
return new CloseIconControl(this.element, this.defs, new PortNode(-1), -1); if (this.cachedCloseIcons[port]) {
return this.cachedCloseIcons[port];
}
const closeIcon = new CloseIconControl(this.element, this.defs, new PortNode(-1), -1);
this.cachedCloseIcons[port] = closeIcon;
return closeIcon;
}
private getBackgroundView(port: number) {
if (this.cachedBackgroundViews[port]) {
return this.cachedBackgroundViews[port];
}
const backgroundView = new BackgroundViewControl(this.element, this.defs, new PortNode(-1), -1);
this.cachedBackgroundViews[port] = backgroundView;
return backgroundView;
} }
private buildDom() { private buildDom() {
@ -392,29 +412,29 @@ namespace pxsim.visuals {
this.layoutView.getLandscapeBrick().kill(); this.layoutView.getLandscapeBrick().kill();
// Save previous inputs for the next cycle // Save previous inputs for the next cycle
EV3View.previousSelectedInputs = ev3board().getInputNodes().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1) EV3View.previousSelectedInputs = {};
EV3View.previousSeletedOutputs = ev3board().getMotors().map((node, index) => (this.getDisplayViewForNode(node.id, index).getSelected()) ? node.id : -1); ev3board().getInputNodes().forEach((node, index) =>
EV3View.previousSelectedInputs[index] = (this.getDisplayViewForNode(node.id, index).getSelected()));
EV3View.previousSeletedOutputs = {};
ev3board().getMotors().forEach((node, index) =>
EV3View.previousSeletedOutputs[index] = (this.getDisplayViewForNode(node.id, index).getSelected()));
EV3View.previousBrickLandscape = this.layoutView.isBrickLandscape(); EV3View.previousBrickLandscape = this.layoutView.isBrickLandscape();
} }
private static previousSelectedInputs: number[]; private static previousSelectedInputs: pxt.Map<boolean> = {};
private static previousSeletedOutputs: number[]; private static previousSeletedOutputs: pxt.Map<boolean> = {};
private static previousBrickLandscape: boolean; private static previousBrickLandscape: boolean;
private static isPreviousInputSelected(index: number, id: number) { private static isPreviousInputSelected(index: number) {
if (EV3View.previousSelectedInputs && EV3View.previousSelectedInputs[index] == id) { const previousInput = EV3View.previousSelectedInputs[index];
EV3View.previousSelectedInputs[index] = undefined; delete EV3View.previousSelectedInputs[index];
return true; return previousInput;
}
return false;
} }
private static isPreviousOutputSelected(index: number, id: number) { private static isPreviousOutputSelected(index: number) {
if (EV3View.previousSeletedOutputs && EV3View.previousSeletedOutputs[index] == id) { const previousOutput = EV3View.previousSeletedOutputs[index];
EV3View.previousSeletedOutputs[index] = undefined; delete EV3View.previousSeletedOutputs[index];
return true; return previousOutput;
}
return false;
} }
private static isPreviousBrickLandscape() { private static isPreviousBrickLandscape() {
@ -463,11 +483,13 @@ namespace pxsim.visuals {
const view = this.getDisplayViewForNode(node.id, index); const view = this.getDisplayViewForNode(node.id, index);
if (!node.didChange() && !view.didChange()) return; if (!node.didChange() && !view.didChange()) return;
if (view) { if (view) {
const isSelected = EV3View.isPreviousInputSelected(index, node.id) || view.getSelected(); const previousSelected = EV3View.isPreviousInputSelected(index);
if (isSelected && !view.getSelected()) view.setSelected(true); const isSelected = previousSelected != undefined ? previousSelected : view.getSelected();
view.setSelected(isSelected);
const control = isSelected ? this.getControlForNode(node.id, index, !node.modeChange()) : undefined; const control = isSelected ? this.getControlForNode(node.id, index, !node.modeChange()) : undefined;
const closeIcon = control && view.hasClose() ? this.getCloseIconView() : undefined; const closeIcon = control ? this.getCloseIconView(index + 10) : undefined;
this.layoutView.setInput(index, view, control, closeIcon); const backgroundView = control && view.hasBackground() ? this.getBackgroundView(index + 10) : undefined;
this.layoutView.setInput(index, view, control, closeIcon, backgroundView);
view.updateState(); view.updateState();
if (control) control.updateState(); if (control) control.updateState();
} }
@ -485,11 +507,13 @@ namespace pxsim.visuals {
const view = this.getDisplayViewForNode(node.id, index); const view = this.getDisplayViewForNode(node.id, index);
if (!node.didChange() && !view.didChange()) return; if (!node.didChange() && !view.didChange()) return;
if (view) { if (view) {
const isSelected = EV3View.isPreviousOutputSelected(index, node.id) || view.getSelected(); const previousSelected = EV3View.isPreviousOutputSelected(index);
if (isSelected && !view.getSelected()) view.setSelected(true); const isSelected = previousSelected != undefined ? previousSelected : view.getSelected();
view.setSelected(isSelected);
const control = isSelected ? this.getControlForNode(node.id, index) : undefined; const control = isSelected ? this.getControlForNode(node.id, index) : undefined;
const closeIcon = control ? this.getCloseIconView() : undefined; const closeIcon = control ? this.getCloseIconView(index) : undefined;
this.layoutView.setOutput(index, view, control, closeIcon); const backgroundView = control && view.hasBackground() ? this.getBackgroundView(index) : undefined;
this.layoutView.setOutput(index, view, control, closeIcon, backgroundView);
view.updateState(); view.updateState();
if (control) control.updateState(); if (control) control.updateState();
} }

View File

@ -2,8 +2,8 @@
namespace pxsim.visuals { namespace pxsim.visuals {
export const CONTROL_WIDTH = 87.5; export const CONTROL_WIDTH = 76.84;
export const CONTROL_HEIGHT = 175; export const CONTROL_HEIGHT = 112.72;
export const CONTROL_TEXT_COLOR = '#000'; export const CONTROL_TEXT_COLOR = '#000';
@ -43,28 +43,29 @@ namespace pxsim.visuals {
return this.content; return this.content;
} }
public resize(width: number, height: number) { public resize(width: number, height: number, strict?: boolean) {
super.resize(width, height); super.resize(width, height);
this.updateDimensions(width, height); this.updateDimensions(width, height, strict);
} }
private updateDimensions(width: number, height: number) { private updateDimensions(width: number, height: number, strict?: boolean) {
if (this.content) { if (this.content) {
const currentWidth = this.getInnerWidth(); const currentWidth = this.getInnerWidth();
const currentHeight = this.getInnerHeight(); const currentHeight = this.getInnerHeight();
const newHeight = currentHeight / currentWidth * width; const newHeight = currentHeight / currentWidth * width;
const newWidth = currentWidth / currentHeight * height; const newWidth = currentWidth / currentHeight * height;
if (newHeight > height) { if (strict) {
this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${height}`);
} else if (newHeight > height) {
// scale width instead // scale width instead
this.content.setAttribute('width', `${newWidth}`); this.content.setAttribute('width', `${newWidth}`);
this.content.setAttribute('height', `${height}`); this.content.setAttribute('height', `${height}`);
// translate to the middle (width) this.width = newWidth;
this.translate(width / 2 - newWidth / 2, 0);
} else { } else {
this.content.setAttribute('width', `${width}`); this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`); this.content.setAttribute('height', `${newHeight}`);
// translate to the middle (height) this.height = newHeight;
this.translate(0, height / 2 - newHeight / 2);
} }
} }
} }

View File

@ -0,0 +1,42 @@
namespace pxsim.visuals {
export class BackgroundViewControl extends ControlView<PortNode> {
private backgroundGroup: SVGGElement;
private backgroundRect: SVGRectElement;
getInnerView() {
this.backgroundGroup = svg.elt("g") as SVGGElement;
this.backgroundRect = pxsim.svg.child(this.backgroundGroup,
"rect", {
'x': 0, 'y': 0,
'width': '100%',
'height': '100%',
'style': `fill: #d6edff; stroke: #A8A9A8; stroke-width: 3px; stroke-opacity: 0.2`
}) as SVGRectElement;
return this.backgroundGroup;
}
buildDom(): SVGElement {
this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement;
this.content.appendChild(this.getInnerView());
return this.content;
}
public resize(width: number, height: number, strict?: boolean) {
super.resize(width, height, strict);
this.backgroundRect.setAttribute('stroke-dasharray', `${height + width + height} 1000`);
this.backgroundRect.setAttribute('stroke-dashoffset', `-${width}`);
}
getInnerWidth(): number {
return 76.84;
}
getInnerHeight(): number {
return 173.86;
}
}
}

View File

@ -9,7 +9,7 @@ namespace pxsim.visuals {
this.closeGroup = svg.elt("g") as SVGGElement; this.closeGroup = svg.elt("g") as SVGGElement;
this.closeGroup.style.cursor = 'pointer'; this.closeGroup.style.cursor = 'pointer';
const circleCloseWrapper = pxsim.svg.child(this.closeGroup, "g"); const circleCloseWrapper = pxsim.svg.child(this.closeGroup, "g");
pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "16", 'style': "fill: #fff" }); pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "16", 'style': "fill: transparent;" });
pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "15", 'style': "fill: none;stroke: #a8aaa8;stroke-width: 2px" }); pxsim.svg.child(circleCloseWrapper, "circle", { 'cx': "16", 'cy': "16", 'r': "15", 'style': "fill: none;stroke: #a8aaa8;stroke-width: 2px" });
pxsim.svg.child(this.closeGroup, "rect", { 'x': "10", 'y': "16", 'width': "18", 'height': "2", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" }); pxsim.svg.child(this.closeGroup, "rect", { 'x': "10", 'y': "16", 'width': "18", 'height': "2", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
pxsim.svg.child(this.closeGroup, "rect", { 'x': "18", 'y': "8", 'width': "2", 'height': "18", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" }); pxsim.svg.child(this.closeGroup, "rect", { 'x': "18", 'y': "8", 'width': "2", 'height': "18", 'transform': "translate(-9.46 17.41) rotate(-45)", 'style': "fill: #a8aaa8" });
@ -18,15 +18,11 @@ namespace pxsim.visuals {
} }
buildDom(): SVGElement { buildDom(): SVGElement {
this.content = svg.elt("svg", { width: "100%", height: "100%"}) as SVGSVGElement; this.content = svg.elt("svg", { width: "100%", height: "100%", viewBox:"0 0 32 32"}) as SVGSVGElement;
this.content.appendChild(this.getInnerView()); this.content.appendChild(this.getInnerView());
return this.content; return this.content;
} }
public resize(width: number, height: number) {
super.resize(width, height);
}
public getInnerHeight() { public getInnerHeight() {
return 32; return 32;
} }

View File

@ -5,28 +5,32 @@ namespace pxsim.visuals {
export class ColorGridControl extends ControlView<ColorSensorNode> { export class ColorGridControl extends ControlView<ColorSensorNode> {
private group: SVGGElement; private group: SVGGElement;
private static colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey', 'white'];
private static colorValue = [5, 4, 2, 3, 1, 7, 6];
getInnerView() { getInnerView() {
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`) this.group.setAttribute("transform", `translate(2, 2.5) scale(0.6)`)
const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00']; const colors = ['#f12a21', '#ffd01b', '#006db3', '#00934b', '#000', '#6c2d00'];
const colorValue = [5, 4, 2, 3, 1, 7]; const colorIds = ['red', 'yellow', 'blue', 'green', 'black', 'grey'];
let cy = -4; let cy = -4;
for (let c = 0; c < colorIds.length; c++) { for (let c = 0; c < colorIds.length; c++) {
const cx = c % 2 == 0 ? 2.2 : 7.5; const cx = c % 2 == 0 ? 2.2 : 7.5;
if (c % 2 == 0) cy += 5; if (c % 2 == 0) cy += 5;
if (colorIds[c]) { if (colorIds[c]) {
const circle = pxsim.svg.child(this.group, "circle", { 'class': 'sim-color-grid-circle', 'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` }); const circle = pxsim.svg.child(this.group, "circle", {
'class': `sim-color-grid-circle sim-color-grid-${colorIds[c]}`,
'cx': cx, 'cy': cy, 'r': '2', 'style': `fill: ${colors[c]}` });
pointerEvents.down.forEach(evid => circle.addEventListener(evid, ev => { pointerEvents.down.forEach(evid => circle.addEventListener(evid, ev => {
this.setColor(colorValue[c]); this.setColor(ColorGridControl.colorValue[c]);
})); }));
} }
} }
const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' }); const whiteCircleWrapper = pxsim.svg.child(this.group, "g", { 'id': 'white-cirlce-wrapper' });
pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` }); pxsim.svg.child(whiteCircleWrapper, "circle", { 'class': 'sim-color-grid-circle sim-color-grid-white', 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: #fff` });
pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` }); pxsim.svg.child(whiteCircleWrapper, "circle", { 'cx': 2.2, 'cy': '16', 'r': '2', 'style': `fill: none;stroke: #94989b;stroke-width: 0.1px` });
pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => { pointerEvents.down.forEach(evid => whiteCircleWrapper.addEventListener(evid, ev => {
this.setColor(6); this.setColor(6);
@ -42,9 +46,34 @@ namespace pxsim.visuals {
return 15; return 15;
} }
public updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const color = node.getValue();
for (let c = 0; c < ColorGridControl.colorValue.length; c++) {
const colorId = ColorGridControl.colorIds[c];
const colorValue = ColorGridControl.colorValue[c];
const colorDiv = this.group.getElementsByClassName(`sim-color-grid-${colorId}`)[0] as HTMLElement;
if (colorValue == color) {
pxsim.U.addClass(colorDiv, 'sim-color-selected');
} else {
pxsim.U.removeClass(colorDiv, 'sim-color-selected');
}
}
}
private setColor(color: number) { private setColor(color: number) {
const state = this.state; const state = this.state;
state.setColor(color); const currentColor = state.getValue();
if (currentColor == color) {
state.setColor(0);
} else {
state.setColor(color);
}
} }
} }
} }

View File

@ -15,7 +15,7 @@ namespace pxsim.visuals {
} }
private getReporterHeight() { private getReporterHeight() {
return 58; return 38;
} }
private getSliderWidth() { private getSliderWidth() {
@ -23,10 +23,10 @@ namespace pxsim.visuals {
} }
private getSliderHeight() { private getSliderHeight() {
return 111; return 131;
} }
private getMax() { private getMaxValue() {
return 100; return 100;
} }
@ -36,9 +36,9 @@ namespace pxsim.visuals {
} }
const node = this.state; const node = this.state;
const percentage = node.getValue(); const percentage = node.getValue();
const inversePercentage = this.getMax() - percentage; const inversePercentage = this.getMaxValue() - percentage;
svg.setGradientValue(this.colorGradient, inversePercentage + "%"); svg.setGradientValue(this.colorGradient, inversePercentage + "%");
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`; this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}%`;
} }
updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) { updateColorLevel(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
@ -47,7 +47,7 @@ namespace pxsim.visuals {
const height = bBox.height; const height = bBox.height;
let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height)); let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
const state = this.state; const state = this.state;
state.setColor(t * this.getMax()); state.setColor(t * this.getMaxValue());
} }
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) { getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
@ -60,7 +60,7 @@ namespace pxsim.visuals {
svg.setGradientColors(this.colorGradient, "black", "yellow"); svg.setGradientColors(this.colorGradient, "black", "yellow");
const reporterGroup = pxsim.svg.child(this.group, "g"); const reporterGroup = pxsim.svg.child(this.group, "g");
reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 50)`); reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`);
this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement; this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
const sliderGroup = pxsim.svg.child(this.group, "g"); const sliderGroup = pxsim.svg.child(this.group, "g");

View File

@ -7,11 +7,59 @@ namespace pxsim.visuals {
private gradient: SVGLinearGradientElement; private gradient: SVGLinearGradientElement;
private slider: SVGGElement; private slider: SVGGElement;
private rect: SVGRectElement;
private reporter: SVGTextElement; private reporter: SVGTextElement;
private static SLIDER_HANDLE_HEIGHT = 26; private static SLIDER_HANDLE_HEIGHT = 26;
private static SLIDER_SIDE_PADDING = 6; private static SLIDER_SIDE_PADDING = 6;
getInnerWidth() {
return 111;
}
getInnerHeight() {
return 192;
}
private getReporterHeight() {
return 38;
}
private getSliderWidth() {
return 62;
}
private getSliderHeight() {
return 131;
}
private getMaxValue() {
return 250; // cm
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue() / 10; /* convert back to cm */
const y = this.getSliderHeight() * percentage / this.getMaxValue();
this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
// Update reporter text
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}cm`;
}
updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
const bBox = this.rect.getBoundingClientRect();
const height = bBox.height;
let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
const state = this.state;
state.setDistance((1 - t) * (this.getMaxValue()));
}
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) { getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
let gid = "gradient-slider-" + this.getPort(); let gid = "gradient-slider-" + this.getPort();
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
@ -28,13 +76,14 @@ namespace pxsim.visuals {
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
const reporterGroup = pxsim.svg.child(this.group, "g"); const reporterGroup = pxsim.svg.child(this.group, "g");
reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 42)`); reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`);
this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement; this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
const sliderGroup = pxsim.svg.child(this.group, "g"); const sliderGroup = pxsim.svg.child(this.group, "g");
sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`) sliderGroup.setAttribute("transform", `translate(${this.getInnerWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': DistanceSliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - DistanceSliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` }); const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': DistanceSliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - DistanceSliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
this.rect = rect as SVGRectElement;
this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement; this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
const sliderInner = pxsim.svg.child(this.slider, "g"); const sliderInner = pxsim.svg.child(this.slider, "g");
@ -48,7 +97,7 @@ namespace pxsim.visuals {
height: this.getInnerHeight(), height: this.getInnerHeight(),
opacity: 0, opacity: 0,
cursor: '-webkit-grab' cursor: '-webkit-grab'
}) }) as SVGRectElement;
let pt = parent.createSVGPoint(); let pt = parent.createSVGPoint();
let captured = false; let captured = false;
@ -72,56 +121,6 @@ namespace pxsim.visuals {
return this.group; return this.group;
} }
getInnerHeight() {
return 192;
}
getInnerWidth() {
return 111;
}
private getReporterHeight() {
return 50;
}
private getSliderHeight() {
return 110;
}
private getSliderWidth() {
return 62;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue() / 10; /* convert back to cm */
const y = this.getSliderHeight() * percentage / this.getMax();
this.slider.setAttribute("transform", `translate(0, ${y - DistanceSliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
// Update reporter text
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
}
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
const height = this.getSliderHeight();
const bBox = this.content.getBoundingClientRect();
let t = Math.max(0, Math.min(1, (DistanceSliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
const state = this.state;
state.setDistance((1 - t) * (this.getMax()));
}
private getMin() {
return 0;
}
private getMax() {
return 250; //cm
}
private getGradientDefinition(): LinearGradientDefinition { private getGradientDefinition(): LinearGradientDefinition {
return { return {
stops: [ stops: [

View File

@ -7,11 +7,60 @@ namespace pxsim.visuals {
private gradient: SVGLinearGradientElement; private gradient: SVGLinearGradientElement;
private slider: SVGGElement; private slider: SVGGElement;
private rect: SVGRectElement;
private reporter: SVGTextElement; private reporter: SVGTextElement;
private static SLIDER_HANDLE_HEIGHT = 26; private static SLIDER_HANDLE_HEIGHT = 26;
private static SLIDER_SIDE_PADDING = 6; private static SLIDER_SIDE_PADDING = 6;
getInnerWidth() {
return 111;
}
getInnerHeight() {
return 192;
}
private getReporterHeight() {
return 38;
}
private getSliderWidth() {
return 62;
}
private getSliderHeight() {
return 131;
}
private getMaxValue() {
return 100;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue();
const y = this.getSliderHeight() * percentage / this.getMaxValue();
this.slider.setAttribute("transform", `translate(0, ${y - ProximitySliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
// Update reporter text
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
}
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
const bBox = this.rect.getBoundingClientRect();
const height = bBox.height;
let t = Math.max(0, Math.min(1, (height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height));
const state = this.state;
const v = Math.floor((1 - t) * (this.getMaxValue()));
state.setPromixity(v);
}
getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) { getInnerView(parent: SVGSVGElement, globalDefs: SVGDefsElement) {
let gid = "gradient-slider-" + this.getId(); let gid = "gradient-slider-" + this.getId();
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
@ -27,13 +76,14 @@ namespace pxsim.visuals {
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
const reporterGroup = pxsim.svg.child(this.group, "g"); const reporterGroup = pxsim.svg.child(this.group, "g");
reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 42)`); reporterGroup.setAttribute("transform", `translate(${this.getWidth() / 2}, 20)`);
this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement; this.reporter = pxsim.svg.child(reporterGroup, "text", { 'text-anchor': 'middle', 'x': 0, 'y': '0', 'class': 'sim-text number large inverted' }) as SVGTextElement;
const sliderGroup = pxsim.svg.child(this.group, "g"); const sliderGroup = pxsim.svg.child(this.group, "g");
sliderGroup.setAttribute("transform", `translate(${this.getWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`) sliderGroup.setAttribute("transform", `translate(${this.getInnerWidth() / 2 - this.getSliderWidth() / 2}, ${this.getReporterHeight()})`)
const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': ProximitySliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - ProximitySliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` }); const rect = pxsim.svg.child(sliderGroup, "rect", { 'x': ProximitySliderControl.SLIDER_SIDE_PADDING, 'y': 2, 'width': this.getSliderWidth() - ProximitySliderControl.SLIDER_SIDE_PADDING * 2, 'height': this.getSliderHeight(), 'style': `fill: url(#${gid})` });
this.rect = rect as SVGRectElement;
this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement; this.slider = pxsim.svg.child(sliderGroup, "g", { "transform": "translate(0,0)" }) as SVGGElement;
const sliderInner = pxsim.svg.child(this.slider, "g"); const sliderInner = pxsim.svg.child(this.slider, "g");
@ -71,57 +121,6 @@ namespace pxsim.visuals {
return this.group; return this.group;
} }
getInnerHeight() {
return 192;
}
getInnerWidth() {
return 111;
}
private getReporterHeight() {
return 50;
}
private getSliderHeight() {
return 110;
}
private getSliderWidth() {
return 62;
}
updateState() {
if (!this.visible) {
return;
}
const node = this.state;
const percentage = node.getValue();
const y = this.getSliderHeight() * percentage / this.getMax();
this.slider.setAttribute("transform", `translate(0, ${y - ProximitySliderControl.SLIDER_HANDLE_HEIGHT / 2})`);
// Update reporter text
this.reporter.textContent = `${parseFloat((percentage).toString()).toFixed(0)}`;
}
private updateSliderValue(pt: SVGPoint, parent: SVGSVGElement, ev: MouseEvent) {
let cur = svg.cursorPoint(pt, parent, ev);
const height = this.getSliderHeight();
const bBox = this.content.getBoundingClientRect();
let t = Math.max(0, Math.min(1, (ProximitySliderControl.SLIDER_HANDLE_HEIGHT + height + bBox.top / this.scaleFactor - cur.y / this.scaleFactor) / height))
const state = this.state;
const v = Math.floor((1 - t) * (this.getMax()));
state.setPromixity(v);
}
private getMin() {
return 0;
}
private getMax() {
return 100;
}
private getGradientDefinition(): LinearGradientDefinition { private getGradientDefinition(): LinearGradientDefinition {
return { return {
stops: [ stops: [

View File

@ -13,10 +13,10 @@ namespace pxsim.visuals {
this.group = svg.elt("g") as SVGGElement; this.group = svg.elt("g") as SVGGElement;
const sliderGroup = pxsim.svg.child(this.group, "g"); const sliderGroup = pxsim.svg.child(this.group, "g");
sliderGroup.setAttribute("transform", `translate(5, ${10 + this.getTopPadding()})`) sliderGroup.setAttribute("transform", `translate(10,0)`);
const rotationLine = pxsim.svg.child(sliderGroup, "g"); const rotationLine = pxsim.svg.child(sliderGroup, "g");
pxsim.svg.child(rotationLine, "path", { 'transform': 'translate(5.11 -31.1)', 'd': 'M68.71,99.5l6.1-8S61.3,79.91,42.69,78.35,12,83.14,6.49,85.63a48.69,48.69,0,0,0-9.6,5.89L3.16,99.3S19.27,87.7,37.51,87.94,68.71,99.5,68.71,99.5Z', 'style': 'fill: #626262' }); pxsim.svg.child(rotationLine, "path", { 'transform': 'translate(7.11 -31.1)', 'd': 'M68.71,99.5l6.1-8S61.3,79.91,42.69,78.35,12,83.14,6.49,85.63a48.69,48.69,0,0,0-9.6,5.89L3.16,99.3S19.27,87.7,37.51,87.94,68.71,99.5,68.71,99.5Z', 'style': 'fill: #626262' });
this.slider = pxsim.svg.child(sliderGroup, "g") as SVGGElement; this.slider = pxsim.svg.child(sliderGroup, "g") as SVGGElement;
const handleInner = pxsim.svg.child(sliderGroup, "g"); const handleInner = pxsim.svg.child(sliderGroup, "g");
@ -52,8 +52,8 @@ namespace pxsim.visuals {
return this.group; return this.group;
} }
private getTopPadding() { getInnerWidth() {
return this.getInnerHeight() / 4; return RotationSliderControl.SLIDER_WIDTH * 1.5;
} }
updateState() { updateState() {

View File

@ -13,6 +13,8 @@ namespace pxsim.visuals {
export const MAX_MODULE_WIDTH = 100; export const MAX_MODULE_WIDTH = 100;
export const CLOSE_ICON_GAP_MULTIPLIER = 0.3;
export interface LayoutElement extends View { export interface LayoutElement extends View {
getId(): number; getId(): number;
getPort(): number; getPort(): number;
@ -33,6 +35,9 @@ namespace pxsim.visuals {
private inputCloseIcons: View[] = []; private inputCloseIcons: View[] = [];
private outputCloseIcons: View[] = []; private outputCloseIcons: View[] = [];
private inputBackgroundViews: View[] = [];
private outputBackgroundViews: View[] = [];
private inputWires: WireView[] = []; private inputWires: WireView[] = [];
private outputWires: WireView[] = []; private outputWires: WireView[] = [];
@ -119,7 +124,7 @@ namespace pxsim.visuals {
else this.setlectBrick(); else this.setlectBrick();
} }
public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View) { public setInput(port: number, view: LayoutElement, control?: View, closeIcon?: View, backgroundView?: View) {
if (this.inputs[port] != view || this.inputControls[port] != control) { if (this.inputs[port] != view || this.inputControls[port] != control) {
if (this.inputs[port]) { if (this.inputs[port]) {
// Remove current input // Remove current input
@ -131,10 +136,12 @@ namespace pxsim.visuals {
} }
this.inputControls[port] = control; this.inputControls[port] = control;
this.inputCloseIcons[port] = closeIcon; this.inputCloseIcons[port] = closeIcon;
this.inputBackgroundViews[port] = backgroundView;
this.inputContainers[port].clear(); this.inputContainers[port].clear();
this.inputContainers[port].addView(view);
if (control && backgroundView) this.inputContainers[port].addView(backgroundView);
this.inputContainers[port].addView(view);
if (control) this.inputContainers[port].addView(control); if (control) this.inputContainers[port].addView(control);
if (view.hasClick()) view.registerClick((ev: any) => { if (view.hasClick()) view.registerClick((ev: any) => {
@ -155,7 +162,7 @@ namespace pxsim.visuals {
this.position(); this.position();
} }
public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View) { public setOutput(port: number, view: LayoutElement, control?: View, closeIcon?: View, backgroundView?: View) {
if (this.outputs[port] != view || this.outputControls[port] != control) { if (this.outputs[port] != view || this.outputControls[port] != control) {
if (this.outputs[port]) { if (this.outputs[port]) {
// Remove current output // Remove current output
@ -167,10 +174,12 @@ namespace pxsim.visuals {
} }
this.outputControls[port] = control; this.outputControls[port] = control;
this.outputCloseIcons[port] = closeIcon; this.outputCloseIcons[port] = closeIcon;
this.outputBackgroundViews[port] = backgroundView;
this.outputContainers[port].clear(); this.outputContainers[port].clear();
this.outputContainers[port].addView(view);
if (control && backgroundView) this.outputContainers[port].addView(backgroundView);
this.outputContainers[port].addView(view);
if (control) this.outputContainers[port].addView(control); if (control) this.outputContainers[port].addView(control);
if (view.hasClick()) view.registerClick((ev: any) => { if (view.hasClick()) view.registerClick((ev: any) => {
@ -277,35 +286,48 @@ namespace pxsim.visuals {
const modulePadding = this.getModulePadding(); const modulePadding = this.getModulePadding();
const moduleSpacing = contentWidth / 4; const moduleSpacing = contentWidth / 4;
const moduleWidth = this.getInnerModuleWidth();
let currentX = this.getModulePadding(); let currentX = this.getModulePadding();
let currentY = 0; let currentY = 0;
this.outputs.forEach((n, i) => { this.outputs.forEach((n, i) => {
this.outputContainers[i].translate(currentX, currentY); this.outputContainers[i].translate(currentX + (this.getAbosluteModuleWidth() - this.getInnerModuleWidth()) / 2, currentY);
if (this.outputs[i]) { if (this.outputs[i]) {
const view = this.outputs[i]; const view = this.outputs[i];
const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio(); const outputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
const desiredOutputWidth = this.getInnerModuleWidth() - outputPadding * 2;
const outputWidth = Math.min(desiredOutputWidth, MAX_MODULE_WIDTH);
const outputHeight = this.getModuleHeight(); const outputHeight = this.getModuleHeight();
const outputWidth = this.getInnerModuleWidth();
// Translate and resize view // Translate and resize view
view.resize(outputWidth, outputHeight); view.resize(outputWidth - outputPadding * 2, outputHeight);
const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth; // const viewHeight = view.getInnerHeight() / view.getInnerWidth() * outputWidth;
view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true); // view.translate(outputPadding + ((desiredOutputWidth - outputWidth) / 2), outputHeight - viewHeight, true);
const viewHeight = view.getActualHeight();
view.translate(outputPadding, outputHeight - viewHeight, true);
// Resize control // Resize control
const control = this.outputControls[i]; const control = this.outputControls[i];
if (control) { if (control) {
control.resize(this.getInnerModuleWidth(), outputHeight); const controlWidth = outputWidth;
const closeIconOffset = (this.getCloseIconSize() * (1 + CLOSE_ICON_GAP_MULTIPLIER));
const controlHeight = outputHeight - closeIconOffset;
control.resize(controlWidth, controlHeight);
control.translate((controlWidth - control.getActualWidth()) / 2,
closeIconOffset + ((controlHeight - control.getActualHeight()) / 2), true)
// Translate close icon // Translate and resize close icon
const closeIcon = this.outputCloseIcons[i]; const closeIcon = this.outputCloseIcons[i];
if (closeIcon) { if (closeIcon) {
const closeIconWidth = closeIcon.getWidth(); const closeIconSize = this.getCloseIconSize();
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, 0); closeIcon.resize(closeIconSize, closeIconSize);
closeIcon.translate((outputWidth - closeIcon.getActualWidth()) / 2, (CLOSE_ICON_GAP_MULTIPLIER * closeIcon.getActualHeight()), true);
} }
} }
// Resize background
const backgroundView = this.inputBackgroundViews[i];
if (backgroundView) {
backgroundView.resize(this.getInnerModuleWidth(), outputHeight);
backgroundView.translate(0, 0, true)
}
} }
currentX += moduleSpacing; currentX += moduleSpacing;
}) })
@ -341,30 +363,41 @@ namespace pxsim.visuals {
currentY += brickHeight + this.getWiringHeight(); currentY += brickHeight + this.getWiringHeight();
this.inputs.forEach((n, i) => { this.inputs.forEach((n, i) => {
this.inputContainers[i].translate(currentX, currentY); this.inputContainers[i].translate(currentX + (this.getAbosluteModuleWidth() - this.getInnerModuleWidth()) / 2, currentY);
if (this.inputs[i]) { if (this.inputs[i]) {
const view = this.inputs[i]; const view = this.inputs[i];
const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio(); const inputPadding = this.getInnerModuleWidth() * view.getPaddingRatio();
const desiredInputWidth = this.getInnerModuleWidth() - inputPadding * 2;
const inputWidth = Math.min(desiredInputWidth, MAX_MODULE_WIDTH);
const inputHeight = this.getModuleHeight(); const inputHeight = this.getModuleHeight();
const inputWidth = this.getInnerModuleWidth();
// Translate and resize view // Translate and resize view
view.resize(inputWidth, inputHeight); view.resize(inputWidth - inputPadding * 2, inputHeight);
view.translate(inputPadding + ((desiredInputWidth - inputWidth) / 2), 0, true); const viewHeight = view.getActualHeight();
view.translate(inputPadding, 0, true);
// Resize control // Resize control
const control = this.inputControls[i]; const control = this.inputControls[i];
if (control) { if (control) {
control.resize(this.getInnerModuleWidth(), inputHeight); const controlWidth = inputWidth;
const controlHeight = inputHeight - viewHeight - (this.getCloseIconSize() * (1 + CLOSE_ICON_GAP_MULTIPLIER));
control.resize(controlWidth, controlHeight);
control.translate((controlWidth - control.getActualWidth()) / 2,
viewHeight + ((controlHeight - control.getActualHeight()) / 2), true)
// Translate and resize close icon // Translate and resize close icon
const closeIcon = this.inputCloseIcons[i]; const closeIcon = this.inputCloseIcons[i];
if (closeIcon) { if (closeIcon) {
const closeIconWidth = closeIcon.getWidth(); const closeIconSize = this.getCloseIconSize();
const closeIconHeight = closeIcon.getHeight(); closeIcon.resize(closeIconSize, closeIconSize);
closeIcon.translate(this.getInnerModuleWidth() / 2 - closeIconWidth / 2, this.getModuleHeight() - closeIconHeight); closeIcon.translate((inputWidth - closeIcon.getActualWidth()) / 2, inputHeight - ((1 + CLOSE_ICON_GAP_MULTIPLIER) * closeIcon.getActualHeight()), true);
} }
}
// Resize background
const backgroundView = this.inputBackgroundViews[i];
if (backgroundView) {
backgroundView.resize(this.getInnerModuleWidth(), inputHeight, true);
backgroundView.translate(0, 0, true)
} }
} }
currentX += moduleSpacing; currentX += moduleSpacing;
@ -394,7 +427,7 @@ namespace pxsim.visuals {
public getModuleBounds() { public getModuleBounds() {
return { return {
width: this.width / 4, width: Math.min(this.getAbosluteModuleWidth(), MAX_MODULE_WIDTH),
height: this.getModuleHeight() height: this.getModuleHeight()
} }
} }
@ -407,8 +440,16 @@ namespace pxsim.visuals {
return this.getModuleBounds().width - (this.getModulePadding() * 2); return this.getModuleBounds().width - (this.getModulePadding() * 2);
} }
public getAbosluteModuleWidth() {
return this.width / 4;
}
public getModuleHeight() { public getModuleHeight() {
return this.height * MODULE_HEIGHT_RATIO; return this.height * MODULE_HEIGHT_RATIO;
} }
public getCloseIconSize() {
return this.getInnerModuleWidth() / 4;
}
} }
} }

View File

@ -5,6 +5,8 @@ namespace pxsim.visuals {
private control: ColorGridControl; private control: ColorGridControl;
private static sensor_hole_id = 'color_sensor_white_big';
constructor(port: number) { constructor(port: number) {
super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port); super(COLOR_SENSOR_SVG, "color", NodeType.ColorSensor, port);
} }
@ -19,7 +21,22 @@ namespace pxsim.visuals {
public updateState() { public updateState() {
super.updateState(); super.updateState();
// TODO: show different color modes
const colorState = ev3board().getInputNodes()[this.port];
if (!colorState) return;
const mode = colorState.getMode();
switch (mode) {
case ColorSensorMode.Colors: this.updateSensorLightVisual('#0062DD'); return; // blue
case ColorSensorMode.Reflected: this.updateSensorLightVisual('#F86262'); return; // red
case ColorSensorMode.Ambient: this.updateSensorLightVisual('#67C3E2'); return; // light blue
}
this.updateSensorLightVisual('#ffffff');
}
private updateSensorLightVisual(color: string) {
const sensorHole = this.content.getElementById(this.normalizeId(ColorSensorView.sensor_hole_id)) as SVGCircleElement;
sensorHole.style.fill = color;
} }
} }
} }

View File

@ -107,7 +107,7 @@ namespace pxsim.visuals {
public attachEvents() { public attachEvents() {
} }
public resize(width: number, height: number) { public resize(width: number, height: number, strict?: boolean) {
super.resize(width, height); super.resize(width, height);
this.updateDimensions(width, height); this.updateDimensions(width, height);
} }
@ -120,6 +120,7 @@ namespace pxsim.visuals {
const newWidth = currentWidth / currentHeight * height; const newWidth = currentWidth / currentHeight * height;
this.content.setAttribute('width', `${width}`); this.content.setAttribute('width', `${width}`);
this.content.setAttribute('height', `${newHeight}`); this.content.setAttribute('height', `${newHeight}`);
this.height = newHeight;
} }
} }
@ -138,16 +139,22 @@ namespace pxsim.visuals {
protected updateOpacity() { protected updateOpacity() {
if (this.rendered) { if (this.rendered) {
const opacity = this.selected ? 0.2 : 1; const opacity = this.selected && this.fadeWhenSelected() ? 0.2 : 1;
if (this.hasClick() && this.opacity != opacity) { if (this.hasClick() && this.opacity != opacity) {
this.opacity = opacity; this.opacity = opacity;
this.setOpacity(this.opacity); this.setOpacity(this.opacity);
}
if (this.hasClick()) {
if (this.selected) this.content.style.cursor = ""; if (this.selected) this.content.style.cursor = "";
else this.content.style.cursor = "pointer"; else this.content.style.cursor = "pointer";
} }
} }
} }
protected fadeWhenSelected() {
return true;
}
protected setOpacity(opacity: number) { protected setOpacity(opacity: number) {
this.element.setAttribute("opacity", `${opacity}`); this.element.setAttribute("opacity", `${opacity}`);
} }

View File

@ -6,14 +6,16 @@ namespace pxsim.visuals {
constructor(xml: string, prefix: string, id: NodeType, port: NodeType) { constructor(xml: string, prefix: string, id: NodeType, port: NodeType) {
super(xml, prefix, id, port); super(xml, prefix, id, port);
// Shown by default
this.selected = true;
} }
public getSelected() { protected fadeWhenSelected() {
return true;
}
public hasClose() {
return false; return false;
} }
public hasBackground() {
return true;
}
} }
} }

View File

@ -130,11 +130,19 @@ namespace pxsim.visuals {
return this.element; return this.element;
} }
public resize(width: number, height: number) { public resize(width: number, height: number, strict?: boolean) {
this.width = width; this.width = width;
this.height = height; this.height = height;
} }
public getActualHeight() {
return this.height;
}
public getActualWidth() {
return this.width;
}
private updateTransform() { private updateTransform() {
if (this.rendered) { if (this.rendered) {
let left = this.left; let left = this.left;
@ -212,10 +220,6 @@ namespace pxsim.visuals {
} }
} }
public hasClose() {
return true;
}
protected setChangedState() { protected setChangedState() {
this.changed = true; this.changed = true;
} }
@ -225,6 +229,10 @@ namespace pxsim.visuals {
this.changed = false; this.changed = false;
return res; return res;
} }
public hasBackground() {
return false;
}
} }
export abstract class SimView<T extends BaseNode> extends View implements LayoutElement { export abstract class SimView<T extends BaseNode> extends View implements LayoutElement {
@ -272,9 +280,13 @@ namespace pxsim.visuals {
} }
public clear() { public clear() {
const markForRemoval: Element[] = [];
forEachElement(this.element.childNodes, e => { forEachElement(this.element.childNodes, e => {
this.element.removeChild(e); markForRemoval.push(e);
}); });
markForRemoval.forEach(e => {
this.element.removeChild(e);
})
} }
public onComponentInjected() { public onComponentInjected() {