mirror of
https://github.com/xibyte/jsketcher
synced 2026-02-23 07:45:08 +01:00
refactor App2d class(terminal and property view), remove legacy UI toolkit and its helpers
This commit is contained in:
parent
557b3474ee
commit
13eb317c89
35 changed files with 748 additions and 1305 deletions
|
|
@ -12,7 +12,7 @@ export default class Window extends React.Component {
|
|||
resizeHelper = new ResizeHelper();
|
||||
|
||||
render() {
|
||||
let {initWidth, initHeight, initLeft, initTop, setFocus, className,
|
||||
let {initWidth, initHeight, initLeft, initTop, initRight, initBottom, setFocus, className,
|
||||
resizeCapturingBuffer, resize,
|
||||
children, title, icon, minimizable, onClose, ...props} = this.props;
|
||||
return <div className={cx(ls.root, className)} {...props} ref={this.keepRef}>
|
||||
|
|
@ -97,7 +97,7 @@ export default class Window extends React.Component {
|
|||
if (el === null) {
|
||||
return;
|
||||
}
|
||||
let {initWidth, initHeight, initLeft, initTop, resize, resizeCapturingBuffer, onResize, ...props} = this.props;
|
||||
let {initWidth, initHeight, initLeft, initTop, initRight, initBottom, resize, resizeCapturingBuffer, onResize, ...props} = this.props;
|
||||
if (initWidth) {
|
||||
el.style.width = initWidth + 'px';
|
||||
}
|
||||
|
|
@ -106,10 +106,13 @@ export default class Window extends React.Component {
|
|||
}
|
||||
if (initLeft) {
|
||||
el.style.left = initLeft + 'px';
|
||||
|
||||
} else if (initRight) {
|
||||
el.style.left = (window.innerWidth - el.offsetWidth - initRight) + 'px';
|
||||
}
|
||||
if (initTop) {
|
||||
el.style.top = initTop + 'px';
|
||||
} else if (initBottom) {
|
||||
el.style.top = (window.innerHeight - el.offsetHeight - initBottom) + 'px';
|
||||
}
|
||||
this.resizeHelper.registerResize(el, resize, resizeCapturingBuffer);
|
||||
this.el = el;
|
||||
|
|
@ -121,10 +124,11 @@ Window.defaultProps = {
|
|||
minimizable: false,
|
||||
};
|
||||
|
||||
class ResizeHelper {
|
||||
export class ResizeHelper {
|
||||
|
||||
constructor () {
|
||||
constructor (controlGlobalListeners = false) {
|
||||
this.moveHandler = null;
|
||||
this.controlGlobalListeners = controlGlobalListeners;
|
||||
}
|
||||
|
||||
captureResize(el, dirMask, e, onResize) {
|
||||
|
|
@ -166,6 +170,20 @@ class ResizeHelper {
|
|||
if (onResize !== undefined) {
|
||||
onResize();
|
||||
}
|
||||
};
|
||||
if (this.controlGlobalListeners) {
|
||||
const moveListener = e => {
|
||||
if (this.moveHandler) {
|
||||
this.moveHandler(e);
|
||||
}
|
||||
};
|
||||
const quitListener = e => {
|
||||
this.moveHandler = null;
|
||||
document.removeEventListener("mousemove", moveListener);
|
||||
document.removeEventListener("mouseup", quitListener);
|
||||
};
|
||||
document.addEventListener("mousemove", moveListener);
|
||||
document.addEventListener("mouseup", quitListener);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export default class NumberControl extends React.Component {
|
|||
}
|
||||
this.input.value = val;
|
||||
onChange(val);
|
||||
e.preventDefault();
|
||||
// e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
.radioButton {
|
||||
margin-left: 1px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
|
@ -8,10 +8,12 @@
|
|||
|
||||
.button-behavior(@color) {
|
||||
&:hover {
|
||||
background-color: @color;
|
||||
background-color: @color;
|
||||
border-color: @color;
|
||||
}
|
||||
&:active {
|
||||
background-color: darken(@color, 20%);
|
||||
border-color: darken(@color, 20%);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'Point On Line',
|
||||
label: 'point & line',
|
||||
icon32: 'ui.styles.init.svg'
|
||||
icon32: 'img/vec/pointOnLine.svg'
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let viewer = services.sketcher.inPlaceEditor.viewer;
|
||||
|
|
@ -165,7 +165,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'Point On Arc / Ellipse',
|
||||
label: 'point & arc',
|
||||
icon32: 'ui.styles.init.svg'
|
||||
icon32: 'img/vec/pointOnArc.svg'
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let viewer = services.sketcher.inPlaceEditor.viewer;
|
||||
|
|
@ -178,7 +178,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'Point In the Middle',
|
||||
label: 'point @ middle',
|
||||
icon32: 'ui.styles.init.svg'
|
||||
icon32: 'img/vec/pointInMiddle.svg'
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let viewer = services.sketcher.inPlaceEditor.viewer;
|
||||
|
|
@ -191,7 +191,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'Angle Between 2 Lines',
|
||||
label: 'angle',
|
||||
icon32: 'ui.styles.init.svg'
|
||||
icon32: 'img/vec/angle.svg'
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let viewer = services.sketcher.inPlaceEditor.viewer;
|
||||
|
|
@ -204,7 +204,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'Symmetry',
|
||||
label: 'symmetry',
|
||||
icon32: 'ui.styles.init.svg'
|
||||
icon32: 'img/vec/symmetry.svg'
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let viewer = services.sketcher.inPlaceEditor.viewer;
|
||||
|
|
@ -228,7 +228,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'Lock Convexity',
|
||||
label: 'convex',
|
||||
icon32: 'ui.styles.init.svg'
|
||||
icon32: 'img/vec/convex.svg'
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let viewer = services.sketcher.inPlaceEditor.viewer;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,20 @@
|
|||
import '../css/app.less'
|
||||
import 'ui/styles/init/index.less';
|
||||
import '../css/app.less'
|
||||
|
||||
import App2D from './sketcher/sketcher-app';
|
||||
import {Layer} from './sketcher/viewer2d';
|
||||
import * as ui from './ui/ui.js';
|
||||
import * as toolkit from './ui/toolkit';
|
||||
import {Constraints} from './sketcher/parametric'
|
||||
import './utils/jqueryfy'
|
||||
|
||||
import ReactDOM from "react-dom";
|
||||
import {SketcherApp} from "./sketcher/components/SketcherApp";
|
||||
import React from "react";
|
||||
import {stream} from "lstream";
|
||||
import {loadUIState, saveUIState} from "./sketcher/uiState";
|
||||
import {Scope} from "./sketcher/components/Scope";
|
||||
import {createElement} from "./utils/domUtils";
|
||||
|
||||
function initializeSketcherApplication() {
|
||||
var app = new App2D();
|
||||
const app = new App2D();
|
||||
window.__CAD_APP = app;
|
||||
var sketchId = app.getSketchId();
|
||||
const sketchId = app.getSketchId();
|
||||
if (sketchId === App2D.STORAGE_PREFIX + '__sample2D__') {
|
||||
// var sample = '{"layers":[{"name":"_dim","style":{"lineWidth":1,"strokeStyle":"#bcffc1","fillStyle":"#00FF00"},"data":[{"id":0,"_class":"TCAD.TWO.DiameterDimension","obj":90},{"id":1,"_class":"TCAD.TWO.DiameterDimension","obj":95},{"id":2,"_class":"TCAD.TWO.DiameterDimension","obj":42},{"id":3,"_class":"TCAD.TWO.Dimension","a":5,"b":8,"flip":false},{"id":4,"_class":"TCAD.TWO.DiameterDimension","obj":105}]},{"name":"sketch","style":{"lineWidth":2,"strokeStyle":"#ffffff","fillStyle":"#000000"},"data":[{"id":11,"_class":"TCAD.TWO.Segment","points":[[5,[6,110.1295615870824],[7,313.66509156975803]],[8,[9,419.44198895058975],[10,516.7065215258621]]]},{"id":18,"_class":"TCAD.TWO.Segment","points":[[12,[13,489.1218947877601],[14,477.98601743930897]],[15,[16,481.90945628911174],[17,182.9391540301952]]]},{"id":25,"_class":"TCAD.TWO.Segment","points":[[19,[20,427.6872468325118],[21,163.96220645927505]],[22,[23,349.9023145352797],[24,256.7344291384989]]]},{"id":32,"_class":"TCAD.TWO.Segment","points":[[26,[27,306.81261277555075],[28,273.1404656521002]],[29,[30,135.09050734792822],[31,247.98348666778958]]]},{"id":42,"_class":"TCAD.TWO.Arc","points":[[33,[34,489.1218947877601],[35,477.98601743930897]],[36,[37,419.44198895058975],[38,516.7065215258621]],[39,[40,444.1353623657045],[41,479.08688157090376]]]},{"id":53,"_class":"TCAD.TWO.Arc","points":[[44,[45,427.6872468325118],[46,163.96220645927505]],[47,[48,481.90945628911174],[49,182.9391540301952]],[50,[51,451.2148840882273],[52,183.68960424767275]]]},{"id":64,"_class":"TCAD.TWO.Arc","points":[[55,[56,349.9023145352797],[57,256.7344291384989]],[58,[59,306.81261277555075],[60,273.1404656521002]],[61,[62,313.6665992835383],[63,226.35256652594512]]]},{"id":75,"_class":"TCAD.TWO.Arc","points":[[66,[67,110.1295615870824],[68,313.66509156975803]],[69,[70,135.09050734792822],[71,247.98348666778958]],[72,[73,129.8749213918784],[74,283.58516027516237]]]},{"id":80,"_class":"TCAD.TWO.Circle","c":[77,[78,444.1353623657045],[79,479.08688157090376]],"r":17},{"id":85,"_class":"TCAD.TWO.Circle","c":[82,[83,451.2148840882273],[84,183.68960424767275]],"r":17},{"id":90,"_class":"TCAD.TWO.Circle","c":[87,[88,129.8749213918784],[89,283.58516027516237]],"r":17},{"id":95,"_class":"TCAD.TWO.Circle","c":[92,[93,364.7627927122075],[94,358.27520724354514]],"r":50},{"id":100,"_class":"TCAD.TWO.Circle","c":[97,[98,450.6425914465028],[99,356.1758703461729]],"r":13},{"id":105,"_class":"TCAD.TWO.Circle","c":[102,[103,281.1241663120215],[104,360.3197585470608]],"r":13}]},{"name":"_construction_","style":{"lineWidth":1,"strokeStyle":"#aaaaaa","fillStyle":"#000000"},"data":[{"id":113,"_class":"TCAD.TWO.Segment","points":[[107,[108,366.96497096679207],[109,448.36204633886825]],[110,[111,362.6842565514955],[112,273.2463262825022]]]},{"id":120,"_class":"TCAD.TWO.Segment","points":[[114,[115,254.60331148100178],[116,360.9680624545806]],[117,[118,474.9222739434132],[119,355.5823520325097]]]}]}],"constraints":[["Tangent",[42,18]],["Tangent",[42,11]],["coi",[33,12]],["coi",[36,8]],["Tangent",[53,25]],["Tangent",[53,18]],["coi",[44,19]],["coi",[47,15]],["Tangent",[64,25]],["Tangent",[64,32]],["coi",[55,22]],["coi",[58,26]],["Tangent",[75,11]],["Tangent",[75,32]],["coi",[66,5]],["coi",[69,29]],["coi",[77,39]],["coi",[82,50]],["coi",[87,72]],["RR",[80,85]],["RR",[85,90]],["parallel",[113,18]],["perpendicular",[120,113]],["Symmetry",[92,120]],["PointOnLine",[92,113]],["PointOnLine",[102,120]],["PointOnLine",[97,120]],["RR",[105,100]]]}';
|
||||
// localStorage.setItem(sketchId, sample);
|
||||
|
|
@ -27,111 +23,12 @@ function initializeSketcherApplication() {
|
|||
app.fit();
|
||||
|
||||
|
||||
|
||||
const constraintsView = app.dock.views['Constraints'];
|
||||
|
||||
// function configureConstraintsFilter() {
|
||||
// var constraintsCaption = constraintsView.node.find('.tool-caption');
|
||||
// var constraintsFilterBtn = ui.faBtn("filter");
|
||||
// constraintsFilterBtn.css({'float': 'right', 'margin-right': '10px', cursor: 'pointer'});
|
||||
// constraintsCaption.append(constraintsFilterBtn);
|
||||
// var constraintsFilterWin = new ui.Window($('#constrFilter'), app.winManager);
|
||||
// ui.bindOpening(constraintsFilterBtn, constraintsFilterWin);
|
||||
// var content = constraintsFilterWin.root.find('.content');
|
||||
//
|
||||
// var constrTypes = [], constrType;
|
||||
// for (var cname in Constraints) {
|
||||
// c = Constraints[cname];
|
||||
// if (c.prototype !== undefined && c.prototype.UI_NAME !== undefined && !c.prototype.aux) {
|
||||
// constrTypes.push(c);
|
||||
// }
|
||||
// }
|
||||
// constrTypes.sort(function (a, b) {
|
||||
// if (a.prototype.NAME == 'coi') {
|
||||
// return b.prototype.NAME == 'coi' ? 0 : -1;
|
||||
// }
|
||||
// return a.prototype.UI_NAME.localeCompare(b.prototype.UI_NAME)
|
||||
// });
|
||||
// for (var i = 0; i < constrTypes.length; i++) {
|
||||
// var c = constrTypes[i];
|
||||
// if (c.prototype !== undefined && c.prototype.UI_NAME !== undefined && !c.prototype.aux) {
|
||||
// var checkbox = $('<input>', {type : 'checkbox', checked : 'checked', value : c.prototype.NAME});
|
||||
// content.append(
|
||||
// $('<label>', { css : {display : 'block', 'white-space' : 'nowrap'}})
|
||||
// .append(checkbox)
|
||||
// .append(c.prototype.UI_NAME)
|
||||
// );
|
||||
// checkbox.change(function(){
|
||||
// var checkbox = $(this);
|
||||
// app.constraintFilter[checkbox.val()] = checkbox.is(':checked') != true;
|
||||
// constrList.refresh();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// configureConstraintsFilter();
|
||||
constraintsView.node.append($('<div id="constraint-list"></div>'));
|
||||
|
||||
|
||||
var addingModeRadio = new toolkit.InlineRadio(['sketch', 'construction'], ['sketch', 'construction'], 0);
|
||||
app.dock.views['Properties'].node.append('<div>Adding Mode</div>').append(addingModeRadio.root);
|
||||
|
||||
addingModeRadio.root.find('input:radio').change(() => {
|
||||
app.viewer.addingRoleMode = addingModeRadio.getValue();
|
||||
});
|
||||
|
||||
var layerSelection = new toolkit.Combo('layerSelection', 'Layer');
|
||||
app.dock.views['Properties'].node.append(layerSelection.root);
|
||||
|
||||
var updateLayersList = function () {
|
||||
var options = '';
|
||||
for (var i = 0; i < app.viewer.layers.length; i++) {
|
||||
var layer = app.viewer.layers[i];
|
||||
options += "<option value='"+layer.name+"'>"+layer.name+"</option>"
|
||||
}
|
||||
layerSelection.select.html(options).val(app.viewer.activeLayer.name);
|
||||
};
|
||||
updateLayersList();
|
||||
app.viewer.bus.subscribe("activeLayer", function() {
|
||||
updateLayersList();
|
||||
});
|
||||
layerSelection.select
|
||||
.mousedown(updateLayersList)
|
||||
.change(function () {
|
||||
var layer = app.viewer.findLayerByName(layerSelection.select.val());
|
||||
if (layer != null) {
|
||||
app.viewer.activeLayer = layer;
|
||||
}
|
||||
});
|
||||
|
||||
var dimScale = new toolkit.Number("Dim Scale", 1, 0.1, 1);
|
||||
dimScale.min = 0.1;
|
||||
app.dock.views['Properties'].node.append(dimScale.root);
|
||||
dimScale.input.on('t-change', function() {
|
||||
app.viewer.dimScale = $(this).val();
|
||||
});
|
||||
app.viewer.bus.subscribe('dimScale', function(value) {
|
||||
dimScale.input.val(value);
|
||||
});
|
||||
var constantTextArea = $('<textarea />', {id: 'dimTextArea', placeholder : 'for example: A = 50', css: {
|
||||
width: '100%',
|
||||
resize: 'vertical',
|
||||
height: 100,
|
||||
background: 'inherit',
|
||||
border : 'none',
|
||||
color: '#C4E1A4'
|
||||
} });
|
||||
app.viewer.params.subscribe('constantDefinition', 'constantTextArea', function(value) {
|
||||
constantTextArea.val(value);
|
||||
})();
|
||||
constantTextArea.bind("change", function() {
|
||||
app.viewer.params.set('constantDefinition', $(this).val(), 'constantTextArea');
|
||||
});
|
||||
|
||||
app.dock.views['Dimensions'].node.append(constantTextArea);
|
||||
constraintsView.node.append(createElement("div", "constraint-list"));
|
||||
app.dock.views['Properties'].node.append(createElement("div", "properties-view"));
|
||||
app.dock.views['Dimensions'].node.append(createElement("div", "dimension-view"));
|
||||
|
||||
loadUIState(app.dock);
|
||||
app.dock.show('Constraints');
|
||||
|
||||
window.addEventListener("beforeunload", () => {
|
||||
saveUIState(app.dock);
|
||||
|
|
@ -153,4 +50,5 @@ function startReact(appCtx) {
|
|||
);
|
||||
}
|
||||
|
||||
$( () => initializeSketcherApplication() );
|
||||
window.addEventListener('DOMContentLoaded', () => initializeSketcherApplication());
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {MdZoomOutMap} from "react-icons/md";
|
||||
import {AiOutlineCopy, AiOutlineExport, AiOutlineFile, AiOutlineFolderOpen, AiOutlineSave} from "react-icons/ai";
|
||||
import * as ui from "../../ui/ui";
|
||||
import {NoIcon} from "../icons/NoIcon";
|
||||
|
||||
export default [
|
||||
|
||||
|
|
@ -86,4 +86,17 @@ export default [
|
|||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'ToggleTerminal',
|
||||
shortName: 'Toggle Terminal',
|
||||
kind: 'Common',
|
||||
description: 'Open/Close Terminal Window',
|
||||
icon: NoIcon,
|
||||
|
||||
invoke: (ctx) => {
|
||||
ctx.ui.$showTerminalRequest.update(shown => shown ? null : 'please open');
|
||||
}
|
||||
|
||||
},
|
||||
]
|
||||
|
|
@ -22,10 +22,11 @@ const ACTIONS = [
|
|||
//keep going here
|
||||
];
|
||||
|
||||
const ALL_ACTIONS = [
|
||||
export const ALL_ACTIONS = [
|
||||
...ALL_CONTEXTUAL_ACTIONS,
|
||||
...ACTIONS
|
||||
];
|
||||
Object.freeze(ALL_ACTIONS);
|
||||
|
||||
const index = {};
|
||||
ALL_ACTIONS.forEach(a => index[a.id] = a);
|
||||
|
|
@ -51,6 +52,10 @@ export function getSketcherAction(actionId) {
|
|||
return index[actionId];
|
||||
}
|
||||
|
||||
export function getAllSketcherActions() {
|
||||
return ALL_ACTIONS;
|
||||
}
|
||||
|
||||
//For backward compatibility
|
||||
export function runActionOrToastWhyNot(actionId, ctx, silent) {
|
||||
const selection = ctx.viewer.selected;
|
||||
|
|
|
|||
|
|
@ -127,4 +127,46 @@ export function GeneratorButton({prefix='', generator: c, ...props}) {
|
|||
|
||||
</div>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function configureConstraintsFilter() {
|
||||
// var constraintsCaption = constraintsView.node.find('.tool-caption');
|
||||
// var constraintsFilterBtn = ui.faBtn("filter");
|
||||
// constraintsFilterBtn.css({'float': 'right', 'margin-right': '10px', cursor: 'pointer'});
|
||||
// constraintsCaption.append(constraintsFilterBtn);
|
||||
// var constraintsFilterWin = new ui.Window($('#constrFilter'), app.winManager);
|
||||
// ui.bindOpening(constraintsFilterBtn, constraintsFilterWin);
|
||||
// var content = constraintsFilterWin.root.find('.content');
|
||||
//
|
||||
// var constrTypes = [], constrType;
|
||||
// for (var cname in Constraints) {
|
||||
// c = Constraints[cname];
|
||||
// if (c.prototype !== undefined && c.prototype.UI_NAME !== undefined && !c.prototype.aux) {
|
||||
// constrTypes.push(c);
|
||||
// }
|
||||
// }
|
||||
// constrTypes.sort(function (a, b) {
|
||||
// if (a.prototype.NAME == 'coi') {
|
||||
// return b.prototype.NAME == 'coi' ? 0 : -1;
|
||||
// }
|
||||
// return a.prototype.UI_NAME.localeCompare(b.prototype.UI_NAME)
|
||||
// });
|
||||
// for (var i = 0; i < constrTypes.length; i++) {
|
||||
// var c = constrTypes[i];
|
||||
// if (c.prototype !== undefined && c.prototype.UI_NAME !== undefined && !c.prototype.aux) {
|
||||
// var checkbox = $('<input>', {type : 'checkbox', checked : 'checked', value : c.prototype.NAME});
|
||||
// content.append(
|
||||
// $('<label>', { css : {display : 'block', 'white-space' : 'nowrap'}})
|
||||
// .append(checkbox)
|
||||
// .append(c.prototype.UI_NAME)
|
||||
// );
|
||||
// checkbox.change(function(){
|
||||
// var checkbox = $(this);
|
||||
// app.constraintFilter[checkbox.val()] = checkbox.is(':checked') != true;
|
||||
// constrList.refresh();
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// configureConstraintsFilter();
|
||||
88
web/app/sketcher/components/Dock.js
Normal file
88
web/app/sketcher/components/Dock.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import {createElement} from "../../utils/domUtils";
|
||||
|
||||
export class Dock {
|
||||
|
||||
constructor(dockEl, switcherEl, viewDefinitions) {
|
||||
this.views = {};
|
||||
this.dockEl = dockEl;
|
||||
function bindClick(dock, switchEl, viewName) {
|
||||
switchEl.addEventListener('click', e => {
|
||||
if (dock.isVisible(viewName)) {
|
||||
dock.hide(viewName);
|
||||
} else {
|
||||
dock.show(viewName);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < viewDefinitions.length; i++) {
|
||||
let viewDef = viewDefinitions[i];
|
||||
let view = {};
|
||||
this.views[viewDef.name] = view;
|
||||
view.node = createElement('div', undefined, 'dock-node');
|
||||
let caption = createElement('div', undefined, 'tool-caption');
|
||||
caption.appendChild(createElement('span', undefined, 'txt', viewDef.name.toUpperCase()));
|
||||
caption.appendChild(createElement('i', undefined, 'fa fa-'+viewDef.icon));
|
||||
view.node.appendChild(caption);
|
||||
// view.node.style.display = 'none';
|
||||
this.dockEl.appendChild(view.node);
|
||||
view.switchBtn = dockBtn(viewDef.name, viewDef.icon);
|
||||
bindClick(this, view.switchBtn, viewDef.name);
|
||||
switcherEl.appendChild(view.switchBtn);
|
||||
}
|
||||
}
|
||||
|
||||
show(viewName) {
|
||||
let view = this.views[viewName];
|
||||
if (view.switchBtn.classList.contains('selected')) {
|
||||
return;
|
||||
}
|
||||
if (this.dockEl.style.display === 'none') {
|
||||
this.dockEl.style.display = 'block';
|
||||
document.body.dispatchEvent(new Event('layout'));
|
||||
}
|
||||
view.node.style.display = 'block';
|
||||
view.switchBtn.classList.add('selected');
|
||||
}
|
||||
|
||||
hide(viewName) {
|
||||
let view = this.views[viewName];
|
||||
if (!view.switchBtn.classList.contains('selected')) {
|
||||
return;
|
||||
}
|
||||
view.node.style.display = 'none';
|
||||
view.switchBtn.classList.remove('selected');
|
||||
if (Array.from(this.dockEl.querySelectorAll('.dock-node').values()).findIndex(node => node.style.display !== 'none') === -1) {
|
||||
this.dockEl.style.display = 'none';
|
||||
document.body.dispatchEvent(new Event('layout'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isVisible(viewName) {
|
||||
return this.views[viewName].switchBtn.classList.contains('selected');
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
state.forEach(viewName => this.show(viewName));
|
||||
}
|
||||
|
||||
getState() {
|
||||
const state = [];
|
||||
Object.keys(this.views).forEach(viewName => {
|
||||
if (this.isVisible(viewName)) {
|
||||
state.push(viewName);
|
||||
}
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function dockBtn(name, icon) {
|
||||
const btn = createElement('span', undefined, 'dock-btn');
|
||||
btn.appendChild(createElement('i', undefined, 'fa fa-' + icon));
|
||||
btn.appendChild(createElement('span', undefined, 'txt', name));
|
||||
return btn;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,12 +1,10 @@
|
|||
import React, {useContext, useEffect, useMemo, useState} from 'react';
|
||||
import React, {useContext, useMemo, useState} from 'react';
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {useStreamWithUpdater} from "ui/effects";
|
||||
import Window from "../../../../modules/ui/components/Window";
|
||||
import * as ui from "../../ui/ui";
|
||||
import Window, {DIRECTIONS} from "ui/components/Window";
|
||||
import App2D from "../sketcher-app";
|
||||
import Stack from "../../../../modules/ui/components/Stack";
|
||||
import {DIRECTIONS} from "../../ui/ui";
|
||||
import Button from "../../../../modules/ui/components/controls/Button";
|
||||
import Stack from "ui/components/Stack";
|
||||
import Button from "ui/components/controls/Button";
|
||||
import {RiDeleteBinLine} from "react-icons/ri";
|
||||
|
||||
export function SketchManager() {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ import {SketcherToolbar} from "./SketcherToolbar";
|
|||
import {sketcherRightToolbarConfig, sketcherTopToolbarConfig} from "../uiConfig";
|
||||
import {SketchManager} from "./SketchManager";
|
||||
import {ExportDialog} from "./ExportDialog";
|
||||
import {SketcherPropertiesView} from "./SketcherPropertiesView";
|
||||
import {SketcherDimensionView} from "./SketcherDimensionsView";
|
||||
import {SketcherTerminal} from "./TerminalView";
|
||||
|
||||
export const SketcherAppContext = React.createContext({});
|
||||
|
||||
|
|
@ -25,6 +28,14 @@ export function SketcherApp({applicationContext}) {
|
|||
<Scope><ConstraintList /></Scope>,
|
||||
document.getElementById('constraint-list')
|
||||
)}
|
||||
{ReactDOM.createPortal(
|
||||
<Scope><SketcherPropertiesView /></Scope>,
|
||||
document.getElementById('properties-view')
|
||||
)}
|
||||
{ReactDOM.createPortal(
|
||||
<Scope><SketcherDimensionView /></Scope>,
|
||||
document.getElementById('dimension-view')
|
||||
)}
|
||||
{ReactDOM.createPortal(
|
||||
<Scope><SketcherToolbar actions={sketcherRightToolbarConfig}/></Scope>,
|
||||
document.getElementById('right-toolbar')
|
||||
|
|
@ -37,6 +48,7 @@ export function SketcherApp({applicationContext}) {
|
|||
<React.Fragment>
|
||||
<Scope><SketchManager /></Scope>
|
||||
<Scope><ExportDialog /></Scope>
|
||||
<Scope><SketcherTerminal /></Scope>
|
||||
</React.Fragment>,
|
||||
document.getElementById('global-windows')
|
||||
)}
|
||||
|
|
|
|||
19
web/app/sketcher/components/SketcherDimensionsView.jsx
Normal file
19
web/app/sketcher/components/SketcherDimensionsView.jsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
import {useStreamWithUpdater} from "ui/effects";
|
||||
|
||||
const style = {
|
||||
width: '100%',
|
||||
resize: 'vertical',
|
||||
height: 100,
|
||||
background: 'inherit',
|
||||
border : 'none',
|
||||
color: '#C4E1A4'
|
||||
};
|
||||
|
||||
export function SketcherDimensionView() {
|
||||
|
||||
const [definitions, setDefinitions] = useStreamWithUpdater(ctx => ctx.viewer.parametricManager.$constantDefinition);
|
||||
|
||||
return <textarea style={style} id='dimTextArea' placeholder='for example: A = 50' value={definitions||''} onChange={e => setDefinitions(e.target.value||null)}/>
|
||||
|
||||
}
|
||||
27
web/app/sketcher/components/SketcherPropertiesView.jsx
Normal file
27
web/app/sketcher/components/SketcherPropertiesView.jsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import React from 'react';
|
||||
import NumberControl from "ui/components/controls/NumberControl";
|
||||
import {useStreamWithUpdater} from "ui/effects";
|
||||
import RadioButtons, {RadioButton} from "ui/components/controls/RadioButtons";
|
||||
import Stack from "ui/components/Stack";
|
||||
import Label from "ui/components/controls/Label";
|
||||
import Field from "ui/components/controls/Field";
|
||||
|
||||
export function SketcherPropertiesView() {
|
||||
|
||||
const [dimScale, setDimScale] = useStreamWithUpdater(ctx => ctx.viewer.streams.dimScale);
|
||||
const [addingMode, setAddingMode] = useStreamWithUpdater(ctx => ctx.viewer.streams.addingRoleMode);
|
||||
|
||||
return <Stack >
|
||||
<Field >
|
||||
<Label>Adding Mode</Label>
|
||||
<RadioButtons value={addingMode||''} onChange={val => setAddingMode(val||null)}>
|
||||
<RadioButton value={''} label='sketch' />
|
||||
<RadioButton value='construction' />
|
||||
</RadioButtons>
|
||||
</Field>
|
||||
<Field >
|
||||
<Label>Dimension Scale</Label>
|
||||
<NumberControl min={0.1} baseStep={0.1} round={1} onChange={setDimScale} value={dimScale} />
|
||||
</Field>
|
||||
</Stack>;
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
@import "~ui/styles/theme.less";
|
||||
@import "~ui/styles/mixins.less";
|
||||
|
||||
@focus-color: #0065dc;
|
||||
@focus-color-secondary: rgba(3, 102, 214, .3);
|
||||
|
|
@ -38,6 +40,8 @@
|
|||
box-shadow: 0 0 0 2px @focus-color-secondary;
|
||||
outline: none;
|
||||
}
|
||||
.button-behavior(@color-neutral);
|
||||
|
||||
padding: 1px;
|
||||
flex-basis: 36px;
|
||||
display: flex;
|
||||
|
|
|
|||
226
web/app/sketcher/components/TerminalView.jsx
Normal file
226
web/app/sketcher/components/TerminalView.jsx
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import React, {useCallback, useContext, useEffect, useRef, useState} from 'react';
|
||||
import {useStreamWithUpdater} from "ui/effects";
|
||||
import Window from "ui/components/Window";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {getAllSketcherActions} from "../actions";
|
||||
import {memoize} from "lodash/function";
|
||||
import ls from './TerminalView.less';
|
||||
import {DIRECTIONS} from "ui/components/Window";
|
||||
|
||||
export function TerminalView({visible, output, addToOutput, onClose, variantsSupplier, commandProcessor}) {
|
||||
|
||||
const [history, setHistory] = useState([]);
|
||||
const [historyPtr, setHistoryPtr] = useState([]);
|
||||
const [input, setInput] = useState('');
|
||||
const [autocomplete, setAutocomplete] = useState([]);
|
||||
const [shown, setShown] = useState(false);
|
||||
const outputRef = useRef(null);
|
||||
const inputRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (outputRef.current) {
|
||||
outputRef.current.scrollTop = outputRef.current.scrollHeight;
|
||||
}
|
||||
|
||||
}, [autocomplete, output.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef.current && visible) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//deferring creation until first time open
|
||||
useEffect(() => {
|
||||
if (visible && !shown) {
|
||||
setShown(true);
|
||||
}
|
||||
}, [visible]);
|
||||
|
||||
if (!shown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const LIMIT = 20;
|
||||
const value = historyPtr === history.length ? input : value;
|
||||
return <Window title='Commands' initWidth={700} initHeight={200} initRight={60} initBottom={35}
|
||||
className='sketcher-window'
|
||||
resize={DIRECTIONS.NORTH | DIRECTIONS.SOUTH | DIRECTIONS.WEST | DIRECTIONS.EAST}
|
||||
onClose={onClose}
|
||||
style={{
|
||||
display: visible ? 'flex' : 'none'
|
||||
}}>
|
||||
|
||||
<div className={`${ls.content} panel`} style={{padding: 0}}>
|
||||
<div className='scroll' ref={outputRef}>
|
||||
{output.map(({kind, text}, key) => <React.Fragment key={key}>
|
||||
<div className={ls[kind || 'text']}>{ kind === 'command' ? '> ' : ''} {text}</div>
|
||||
</React.Fragment>)}
|
||||
{
|
||||
autocomplete.length > 0 && <div className={ls.autocompleteArea}>
|
||||
{autocomplete.slice(0, LIMIT).map(variant => <span key={variant}>{variant}</span>)}
|
||||
{autocomplete.length > LIMIT && <span>... and {autocomplete.length - LIMIT} more</span>}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className={ls.terminalInput}>
|
||||
<input type="text" placeholder="(type a command)"
|
||||
ref={inputRef}
|
||||
value={input}
|
||||
onChange={e => {
|
||||
setHistoryPtr(history.length);
|
||||
setInput(e.target.value)
|
||||
}}
|
||||
onKeyDown={e => {
|
||||
function consumeEvent() {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if (e.keyCode === 9) {
|
||||
const text = e.target.value;
|
||||
let variants = variantsSupplier().filter(v => v.startsWith(text));
|
||||
variants.sort();
|
||||
if (variants.length !== 0) {
|
||||
const shared = sharedStartOfSortedArray(variants);
|
||||
if (shared.length !== text.length) {
|
||||
setInput(shared);
|
||||
}
|
||||
}
|
||||
setAutocomplete(variants);
|
||||
consumeEvent();
|
||||
} else if (e.keyCode === 38) {
|
||||
setHistoryPtr(ptr => Math.max(ptr - 1, 0));
|
||||
consumeEvent();
|
||||
} else if (e.keyCode === 40) {
|
||||
setHistoryPtr(ptr => {
|
||||
if (ptr !== history.length) {
|
||||
Math.min(ptr + 1, history.length - 1)
|
||||
}
|
||||
});
|
||||
consumeEvent();
|
||||
}
|
||||
}}
|
||||
|
||||
onKeyUp={e => {
|
||||
if (e.keyCode === 13) {
|
||||
const command = e.target.value;
|
||||
setAutocomplete([]);
|
||||
setInput('');
|
||||
addToOutput(
|
||||
{
|
||||
kind: 'command',
|
||||
text: command
|
||||
}
|
||||
);
|
||||
const commandStr = command.trim();
|
||||
if (commandStr) {
|
||||
commandProcessor(commandStr, addToOutput);
|
||||
if (history.length === 0 || command !== history[history.length - 1]) {
|
||||
setHistory(history => [...history, command]);
|
||||
}
|
||||
}
|
||||
setHistoryPtr(history.length);
|
||||
}
|
||||
}}
|
||||
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</Window>
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------- //
|
||||
|
||||
|
||||
const getCommands = memoize(allActions => ([
|
||||
...allActions.filter(a => a.command).map(a => a.command),
|
||||
'help'
|
||||
]));
|
||||
|
||||
const byCommand = memoize(allActions => {
|
||||
const out = {};
|
||||
allActions.forEach(a => {
|
||||
if (a.command) {
|
||||
out[a.command] = a;
|
||||
}
|
||||
});
|
||||
return out;
|
||||
});
|
||||
|
||||
const variantsSupplier = () => getCommands(getAllSketcherActions());
|
||||
|
||||
const DEFAULT_COMMAND_HANDLER = (command, println, ctx) => {
|
||||
if (command === 'help') {
|
||||
println({text: getCommands(getAllSketcherActions()).join(', ')});
|
||||
}
|
||||
|
||||
if (ctx.viewer.toolManager.tool.processCommand) {
|
||||
ctx.viewer.toolManager.tool.processCommand(command);
|
||||
return;
|
||||
}
|
||||
|
||||
let action = byCommand(getAllSketcherActions())[command];
|
||||
if (action) {
|
||||
println({text: action.shortName});
|
||||
action.invoke(ctx);
|
||||
} else {
|
||||
try {
|
||||
const output = eval(command);
|
||||
println({text: output});
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function printToSketchTerminal(text, ctx) {
|
||||
ctx.ui.$terminalOutput.update(output => ([...output, {
|
||||
text
|
||||
}]));
|
||||
}
|
||||
|
||||
const commandHandlerStack = [DEFAULT_COMMAND_HANDLER];
|
||||
|
||||
export function captureSketcherTerminal(handler) {
|
||||
commandHandlerStack.push(handler);
|
||||
}
|
||||
|
||||
export function releaseSketcherTerminal(handler) {
|
||||
if (commandHandlerStack.length > 1) {
|
||||
commandHandlerStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
export function SketcherTerminal() {
|
||||
const [request, setRequest] = useStreamWithUpdater(ctx => ctx.ui.$showTerminalRequest);
|
||||
const [output, setOutput] = useStreamWithUpdater(ctx => ctx.ui.$terminalOutput);
|
||||
|
||||
const ctx = useContext(SketcherAppContext);
|
||||
|
||||
const addToOutput = useCallback(line => setOutput(output => {
|
||||
output.push(line);
|
||||
return output;
|
||||
}), [setOutput]);
|
||||
|
||||
useEffect(() => {
|
||||
ctx.viewer.referencePoint.visible = !!request;
|
||||
ctx.viewer.refresh();
|
||||
}, [request]);
|
||||
|
||||
return <TerminalView visible={!!request}
|
||||
onClose={() => setRequest(null)}
|
||||
variantsSupplier={variantsSupplier}
|
||||
output={output}
|
||||
addToOutput={addToOutput}
|
||||
commandProcessor={(command, println) => commandHandlerStack[commandHandlerStack.length - 1](command, println, ctx)}
|
||||
/>
|
||||
}
|
||||
|
||||
function sharedStartOfSortedArray(array) {
|
||||
let a1 = array[0], a2 = array[array.length - 1], L = a1.length, i = 0;
|
||||
while (i < L && a1.charAt(i) === a2.charAt(i)) i++;
|
||||
return a1.substring(0, i);
|
||||
}
|
||||
39
web/app/sketcher/components/TerminalView.less
Normal file
39
web/app/sketcher/components/TerminalView.less
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
.content {
|
||||
font-family: Monaco, monospace;
|
||||
color: #C4E1A4;
|
||||
font-size: 11px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.terminalInput input {
|
||||
color: #C4E1A4;
|
||||
background: inherit;
|
||||
outline: none;
|
||||
border: 0;
|
||||
margin-top: 4px;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.terminalInput input::-webkit-input-placeholder {
|
||||
color: #777777;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.command {
|
||||
color: #777777;
|
||||
}
|
||||
|
||||
.autocompleteArea {
|
||||
font-style: italic;
|
||||
display: flex;
|
||||
span {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
|
@ -244,7 +244,7 @@ IO.prototype._loadSketch = function(sketch) {
|
|||
}
|
||||
let constants = sketch.constants;
|
||||
if (constants !== undefined) {
|
||||
this.viewer.params.constantDefinition = constants;
|
||||
this.viewer.parametricManager.$constantDefinition.next(constants);
|
||||
}
|
||||
|
||||
this.viewer.parametricManager.finishTransaction();
|
||||
|
|
@ -421,10 +421,15 @@ IO.prototype._serializeSketch = function(metadata) {
|
|||
sketch.stages.push(stageOut);
|
||||
}
|
||||
|
||||
var constantDefinition = this.viewer.params.constantDefinition;
|
||||
const constantDefinition = this.viewer.parametricManager.constantDefinition;
|
||||
if (constantDefinition !== undefined && constantDefinition != null && !/^\s*$/.test(constantDefinition)) {
|
||||
sketch.constants = constantDefinition;
|
||||
}
|
||||
sketch.scene = {
|
||||
dx: this.viewer.translate.x,
|
||||
dy: this.viewer.translate.y,
|
||||
scale: this.viewer.scale,
|
||||
};
|
||||
sketch.metadata = metadata;
|
||||
sketch.version = 2;
|
||||
return sketch;
|
||||
|
|
|
|||
|
|
@ -28,10 +28,12 @@ class ParametricManager {
|
|||
pointer: -1
|
||||
});
|
||||
|
||||
$constantDefinition = state('');
|
||||
|
||||
constructor(viewer) {
|
||||
this.viewer = viewer;
|
||||
this.viewer.params.define('constantDefinition', null);
|
||||
this.viewer.params.subscribe('constantDefinition', 'parametricManager', this.onConstantsExternalChange, this)();
|
||||
|
||||
this.$constantDefinition.attach((def) => this.rebuildConstantTable(def))
|
||||
|
||||
this.reset();
|
||||
|
||||
|
|
@ -61,6 +63,10 @@ class ParametricManager {
|
|||
return this.stage.algNumSystem;
|
||||
}
|
||||
|
||||
get constantDefinition() {
|
||||
return this.$constantDefinition.value;
|
||||
}
|
||||
|
||||
startTransaction() {
|
||||
this.inTransaction = true;
|
||||
for (let stage of this.stages) {
|
||||
|
|
@ -110,7 +116,6 @@ class ParametricManager {
|
|||
this.constantTable[constant] = value;
|
||||
prefix += "const " + constant + " = " + value + ";\n"
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -122,16 +127,14 @@ class ParametricManager {
|
|||
};
|
||||
|
||||
defineNewConstant(name, value) {
|
||||
let constantDefinition = this.viewer.params.constantDefinition;
|
||||
let constantDefinition = this.constantDefinition;
|
||||
let constantText = name + ' = ' + value;
|
||||
if (constantDefinition) {
|
||||
constantDefinition += '\n' + constantText;
|
||||
} else {
|
||||
constantDefinition = constantText;
|
||||
}
|
||||
this.rebuildConstantTable(constantDefinition);
|
||||
//disabling onConstantsExternalChange since we don't need re-solve
|
||||
this.viewer.params.set('constantDefinition', constantDefinition, 'parametricManager');
|
||||
this.$constantDefinition.next(constantDefinition);
|
||||
};
|
||||
|
||||
updateConstraintConstants(constr) {
|
||||
|
|
|
|||
|
|
@ -1,105 +1,162 @@
|
|||
import {Viewer} from './viewer2d.js'
|
||||
import * as ui from '../ui/ui'
|
||||
import {Terminal} from '../ui/terminal'
|
||||
import {BBox, IO} from './io'
|
||||
import {InputManager} from './input-manager'
|
||||
import genSerpinski from '../utils/genSerpinski';
|
||||
import React from "react";
|
||||
import {stream} from "../../../modules/lstream";
|
||||
import {stream, state} from "lstream";
|
||||
import {Dock, dockBtn} from "./components/Dock";
|
||||
import {DIRECTIONS, ResizeHelper} from "../../../modules/ui/components/Window";
|
||||
import {getSketcherAction} from "./actions";
|
||||
|
||||
function App2D() {
|
||||
var app = this;
|
||||
class App2D {
|
||||
|
||||
this.viewer = new Viewer(document.getElementById('viewer'), IO);
|
||||
this.context = createAppContext(this.viewer, this);
|
||||
this.winManager = new ui.WinManager();
|
||||
this.inputManager = new InputManager(this);
|
||||
constructor() {
|
||||
|
||||
this.constraintFilter = {};
|
||||
this.actions = {};
|
||||
this.commands = {};
|
||||
this.viewer = new Viewer(document.getElementById('viewer'), IO);
|
||||
this.context = createAppContext(this.viewer, this);
|
||||
this.inputManager = new InputManager(this);
|
||||
|
||||
//For debug view
|
||||
this._actionsOrder = [];
|
||||
this.actions = {};
|
||||
|
||||
var dockEl = $('#dock');
|
||||
var buttonGroup = $('#status .button-group');
|
||||
this.dock = new ui.Dock(dockEl, buttonGroup, App2D.views);
|
||||
this.dock.show('Constraints');
|
||||
|
||||
var consoleBtn = ui.dockBtn('Commands', 'list');
|
||||
buttonGroup.append(consoleBtn);
|
||||
this.commandsWin = new ui.Window($('#commands'), this.winManager);
|
||||
this.commandsWin.tileUpRelative = $('#viewer');
|
||||
consoleBtn.click((e) => {
|
||||
this.actions['terminal'].action(e)
|
||||
});
|
||||
$(document).on('mousemove', '#viewer', (e) => {
|
||||
let coord = this.viewer.screenToModel(e);
|
||||
$('.coordinates-info').text(this.viewer.roundToPrecision(coord.x) + " : " + this.viewer.roundToPrecision(coord.y));
|
||||
});
|
||||
this.terminalHandler = undefined;
|
||||
this.terminal = new Terminal(this.commandsWin, (command) => this.handleTerminalInput(command), () => this.getAllCommandList());
|
||||
this.bindToolsToTerminal();
|
||||
|
||||
this.winManager.registerResize(dockEl, ui.DIRECTIONS.EAST, function() {$('body').trigger('layout'); });
|
||||
$('body').on('layout', this.viewer.onWindowResize);
|
||||
|
||||
this.registerAction = function(id, desc, action, command) {
|
||||
app.actions[id] = {id, desc, action};
|
||||
if (command) {
|
||||
app.commands[command] = id;
|
||||
}
|
||||
app._actionsOrder.push(id);
|
||||
};
|
||||
|
||||
function checkForTerminalVisibility() {
|
||||
const terminalVisible = app.commandsWin.root.is(':visible');
|
||||
if (terminalVisible) {
|
||||
app.terminal.scrollToTheEnd();
|
||||
}
|
||||
app.viewer.referencePoint.visible = terminalVisible;
|
||||
this.initNonReactUIParts();
|
||||
}
|
||||
checkForTerminalVisibility();
|
||||
|
||||
this.registerAction('terminal', "Open/Close Terminal Window", function () {
|
||||
app.commandsWin.toggle();
|
||||
checkForTerminalVisibility();
|
||||
app.viewer.refresh();
|
||||
});
|
||||
fit() {
|
||||
|
||||
this.registerAction('undo', "Undo", function () {
|
||||
app.viewer.historyManager.undo();
|
||||
});
|
||||
const bbox = new BBox();
|
||||
this.viewer.accept(obj => {
|
||||
bbox.check(obj);
|
||||
return true;
|
||||
});
|
||||
if (!bbox.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.registerAction('redo', "Redo", function () {
|
||||
app.viewer.historyManager.redo();
|
||||
});
|
||||
const bounds = bbox.bbox;
|
||||
this.viewer.showBounds(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
bbox.inc(20 / this.viewer.scale);
|
||||
this.viewer.showBounds(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
}
|
||||
|
||||
this.registerAction('checkpoint', "Checkpoint", function () {
|
||||
app.viewer.historyManager.checkpoint();
|
||||
});
|
||||
cloneSketch() {
|
||||
let name = prompt("Name for sketch clone");
|
||||
if (name != null) {
|
||||
if (this.isSketchExists(name)) {
|
||||
alert("Sorry, a sketch with the name '" + name + "' already exists. Won't override it.");
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(App2D.STORAGE_PREFIX + name, this.viewer.io.serializeSketch());
|
||||
this.openSketch(name);
|
||||
}
|
||||
}
|
||||
|
||||
isSketchExists(name) {
|
||||
return localStorage.getItem(App2D.STORAGE_PREFIX + name) != null;
|
||||
}
|
||||
|
||||
openSketch(name) {
|
||||
let uri = window.location.href.split("#")[0];
|
||||
if (name !== "untitled") {
|
||||
uri += "#" + name;
|
||||
}
|
||||
let win = window.open(uri, '_blank');
|
||||
win.focus();
|
||||
}
|
||||
|
||||
this.registerAction('solve', "Solve System", function () {
|
||||
app.viewer.parametricManager.solve();
|
||||
app.viewer.refresh();
|
||||
});
|
||||
newSketch() {
|
||||
let name = prompt("Name for sketch");
|
||||
if (name != null) {
|
||||
if (this.isSketchExists(name)) {
|
||||
alert("Sorry, a sketch with the name '" + name + "' already exists. Won't override it.");
|
||||
return;
|
||||
}
|
||||
this.openSketch(name);
|
||||
}
|
||||
}
|
||||
|
||||
this.registerAction('CLEAN UP', "Clean All Draw", function () {
|
||||
app.cleanUpData();
|
||||
app.viewer.refresh();
|
||||
});
|
||||
loadFromLocalStorage() {
|
||||
let sketchId = this.getSketchId();
|
||||
let sketchData = localStorage.getItem(sketchId);
|
||||
if (sketchData != null) {
|
||||
this.viewer.historyManager.init(sketchData);
|
||||
this.viewer.io.loadSketch(sketchData);
|
||||
}
|
||||
this.viewer.repaint();
|
||||
}
|
||||
|
||||
getSketchId() {
|
||||
let id = window.location.hash.substring(1);
|
||||
if (!id) {
|
||||
id = "untitled";
|
||||
}
|
||||
return App2D.STORAGE_PREFIX + id;
|
||||
}
|
||||
|
||||
this.registerAction('genSerpinski', "Generate Serpinki Triangle off of a segment", function () {
|
||||
genSerpinski(app.viewer);
|
||||
});
|
||||
printToTerminal(text) {
|
||||
this.context.ui.$terminalOutput.mutate(output => output.push({
|
||||
text
|
||||
}));
|
||||
}
|
||||
|
||||
initNonReactUIParts() {
|
||||
|
||||
//Keep all legacy UI artifacts here.
|
||||
|
||||
const dockEl = document.getElementById('dock');
|
||||
const bottomButtonGroup = document.querySelector('#status .button-group');
|
||||
this.dock = new Dock(dockEl, bottomButtonGroup, AppDockViews);
|
||||
this.dock.show('Constraints');
|
||||
|
||||
const resizeHelper = new ResizeHelper(true);
|
||||
resizeHelper.registerResize(dockEl, DIRECTIONS.EAST, 5, () => document.body.dispatchEvent(new Event('layout')));
|
||||
|
||||
document.body.addEventListener('layout', this.viewer.onWindowResize);
|
||||
|
||||
const consoleBtn = dockBtn('Commands', 'list');
|
||||
bottomButtonGroup.appendChild(consoleBtn);
|
||||
|
||||
consoleBtn.addEventListener('click', () => {
|
||||
getSketcherAction('ToggleTerminal').invoke(this.context);
|
||||
});
|
||||
this.context.ui.$showTerminalRequest.attach(show => {
|
||||
if (show) {
|
||||
consoleBtn.classList.add('selected');
|
||||
} else {
|
||||
consoleBtn.classList.remove('selected');
|
||||
}
|
||||
});
|
||||
|
||||
const coordInfo = document.querySelector('.coordinates-info');
|
||||
this.viewer.canvas.addEventListener('mousemove', e => {
|
||||
const coord = this.viewer.screenToModel(e);
|
||||
coordInfo.innerText = this.viewer.roundToPrecision(coord.x) + " : " + this.viewer.roundToPrecision(coord.y);
|
||||
});
|
||||
|
||||
this.atatchToToolStreams();
|
||||
|
||||
}
|
||||
|
||||
atatchToToolStreams() {
|
||||
|
||||
this.viewer.streams.tool.$change.attach(tool => {
|
||||
document.querySelectorAll('.tool-info').forEach(e => e.innerText = tool.name);
|
||||
document.querySelectorAll('.tool-hint').forEach(e => e.innerText = '');
|
||||
});
|
||||
this.viewer.streams.tool.$change.attach(tool => {
|
||||
document.querySelectorAll('.tool-info').forEach(e => e.innerText = tool.name);
|
||||
document.querySelectorAll('.tool-hint').forEach(e => e.innerText = '');
|
||||
});
|
||||
|
||||
this.viewer.streams.tool.$message.attach((message) => {
|
||||
this.printToTerminal(message);
|
||||
});
|
||||
this.viewer.streams.tool.$hint.attach((message) => {
|
||||
this.printToTerminal(message);
|
||||
document.querySelectorAll('.tool-hint').forEach(e => e.innerText = message);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
App2D.views = [
|
||||
const AppDockViews = [
|
||||
{
|
||||
name: 'Dimensions',
|
||||
icon: 'arrows-v'
|
||||
|
|
@ -111,136 +168,9 @@ App2D.views = [
|
|||
{
|
||||
name: 'Constraints',
|
||||
icon: 'cogs'
|
||||
},
|
||||
{
|
||||
name: 'Mirroring',
|
||||
icon: 'mirror'
|
||||
}
|
||||
];
|
||||
|
||||
App2D.prototype.fit = function() {
|
||||
|
||||
var bbox = new BBox();
|
||||
|
||||
for (var l = 0; l < this.viewer.layers.length; ++l) {
|
||||
var layer = this.viewer.layers[l];
|
||||
for (var i = 0; i < layer.objects.length; ++i) {
|
||||
var obj = layer.objects[i];
|
||||
bbox.check(obj);
|
||||
}
|
||||
}
|
||||
if (!bbox.isValid()) {
|
||||
return;
|
||||
}
|
||||
var bounds = bbox.bbox;
|
||||
this.viewer.showBounds(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
bbox.inc(20 / this.viewer.scale);
|
||||
this.viewer.showBounds(bounds[0], bounds[1], bounds[2], bounds[3]);
|
||||
};
|
||||
|
||||
App2D.prototype.cloneSketch = function() {
|
||||
var name = prompt("Name for sketch clone");
|
||||
if (name != null) {
|
||||
if (this.isSketchExists(name)) {
|
||||
alert("Sorry, a sketch with the name '" + name + "' already exists. Won't override it.");
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(App2D.STORAGE_PREFIX + name, this.viewer.io.serializeSketch());
|
||||
this.openSketch(name);
|
||||
}
|
||||
};
|
||||
|
||||
App2D.prototype.isSketchExists = function(name) {
|
||||
return localStorage.getItem(App2D.STORAGE_PREFIX + name) != null;
|
||||
};
|
||||
|
||||
App2D.prototype.openSketch = function(name) {
|
||||
var uri = window.location.href.split("#")[0];
|
||||
if (name !== "untitled") {
|
||||
uri += "#" + name;
|
||||
}
|
||||
var win = window.open(uri, '_blank');
|
||||
win.focus();
|
||||
};
|
||||
|
||||
App2D.prototype.newSketch = function() {
|
||||
var name = prompt("Name for sketch");
|
||||
if (name != null) {
|
||||
if (this.isSketchExists(name)) {
|
||||
alert("Sorry, a sketch with the name '" + name + "' already exists. Won't override it.");
|
||||
return;
|
||||
}
|
||||
this.openSketch(name);
|
||||
}
|
||||
};
|
||||
|
||||
App2D.prototype.loadFromLocalStorage = function() {
|
||||
var sketchId = this.getSketchId();
|
||||
var sketchData = localStorage.getItem(sketchId);
|
||||
if (sketchData != null) {
|
||||
this.viewer.historyManager.init(sketchData);
|
||||
this.viewer.io.loadSketch(sketchData);
|
||||
}
|
||||
this.viewer.repaint();
|
||||
};
|
||||
|
||||
App2D.prototype.getSketchId = function() {
|
||||
var id = window.location.hash.substring(1);
|
||||
if (!id) {
|
||||
id = "untitled";
|
||||
}
|
||||
return App2D.STORAGE_PREFIX + id;
|
||||
};
|
||||
|
||||
App2D.prototype.bindToolsToTerminal = function() {
|
||||
const toolCommandProcessor = (command) => this.viewer.toolManager.tool.processCommand(command);
|
||||
this.viewer.bus.subscribe('tool-change', () => {
|
||||
var tool = this.viewer.toolManager.tool;
|
||||
this.terminalHandler = tool.processCommand ? toolCommandProcessor : undefined;
|
||||
$('.tool-info').text('tool: ' + tool.name);
|
||||
$('.tool-hint').text('');
|
||||
})();
|
||||
this.viewer.bus.subscribe('tool-message', (message) => {
|
||||
this.terminal.print(message);
|
||||
});
|
||||
this.viewer.bus.subscribe('tool-hint', (message) => {
|
||||
this.terminal.print(message);
|
||||
$('.tool-hint').text(message);
|
||||
});
|
||||
};
|
||||
|
||||
App2D.STATIC_COMMANDS = {
|
||||
"time" : () => new Date(),
|
||||
"help" : (app) => app.getAllCommandList().join(", ")
|
||||
};
|
||||
|
||||
App2D.prototype.getAllCommandList = function() {
|
||||
const commands = Object.keys(this.commands);
|
||||
commands.push.apply(commands, Object.keys(App2D.STATIC_COMMANDS));
|
||||
commands.sort();
|
||||
return commands;
|
||||
};
|
||||
|
||||
App2D.prototype.handleTerminalInput = function(commandStr) {
|
||||
commandStr = commandStr.trim();
|
||||
if (this.terminalHandler) {
|
||||
return this.terminalHandler(commandStr);
|
||||
} else {
|
||||
let cmd = App2D.STATIC_COMMANDS[commandStr];
|
||||
if (cmd) {
|
||||
return cmd(this);
|
||||
}
|
||||
let actionId = this.commands[commandStr];
|
||||
if (actionId) {
|
||||
this.actions[actionId].action();
|
||||
} else {
|
||||
try {
|
||||
return eval(commandStr);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function createAppContext(viewer, app) {
|
||||
return {
|
||||
|
|
@ -250,7 +180,9 @@ function createAppContext(viewer, app) {
|
|||
$constraintEditRequest: stream(),
|
||||
$wizardRequest: stream(),
|
||||
$sketchManagerRequest: stream(),
|
||||
$exportDialogRequest: stream()
|
||||
$exportDialogRequest: stream(),
|
||||
$showTerminalRequest: state(null),
|
||||
$terminalOutput: state([])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ export default function(viewer) {
|
|||
streams.addingRoleMode = state(null);
|
||||
streams.selection = state([]);
|
||||
streams.objectUpdate = stream();
|
||||
streams.dimScale = state(1);
|
||||
streams.tool = {
|
||||
$change: stream(),
|
||||
$message: stream(),
|
||||
$hint: stream()
|
||||
};
|
||||
|
||||
return streams;
|
||||
};
|
||||
|
|
@ -79,7 +79,7 @@ export class ToolManager {
|
|||
|
||||
switchTool(tool) {
|
||||
this.tool = tool;
|
||||
this.viewer.bus.dispatch("tool-change");
|
||||
this.viewer.streams.tool.$change.next(tool);
|
||||
}
|
||||
|
||||
releaseControl() {
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ export class Tool {
|
|||
keyup(e) {};
|
||||
|
||||
sendMessage(text) {
|
||||
this.viewer.bus.dispatch('tool-message', text);
|
||||
this.viewer.streams.tool.$message.next(text);
|
||||
};
|
||||
|
||||
sendHint(hint) {
|
||||
this.viewer.bus.dispatch('tool-hint', hint);
|
||||
this.viewer.streams.tool.$hint.next(hint);
|
||||
};
|
||||
|
||||
sendSpecifyPointHint() {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import constraintGlobalActions from "./actions/constraintGlobalActions";
|
|||
import measureActions from "./actions/measureActions";
|
||||
import toolActions from "./actions/toolActions";
|
||||
import commonActions from "./actions/commonActions";
|
||||
import {removeInPlace} from "../../../modules/gems/iterables";
|
||||
|
||||
export const sketcherRightToolbarConfig = constraintGlobalActions.map(a => a.id);
|
||||
|
||||
|
|
@ -22,4 +23,6 @@ function insertAfter(arr, item, toAdd) {
|
|||
if (index !== -1) {
|
||||
arr.splice(index+1, 0, toAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeInPlace(sketcherTopToolbarConfig, 'ToggleTerminal');
|
||||
|
|
@ -3,8 +3,8 @@ export function saveUIState(dock) {
|
|||
const state = {
|
||||
};
|
||||
|
||||
state.dockWidth = Math.round(dock.dockEl.get(0).offsetWidth);
|
||||
state.views = dock.getState();
|
||||
state.dockWidth = Math.round(dock.dockEl.offsetWidth);
|
||||
// state.views = dock.getState();
|
||||
|
||||
const dimTextArea = document.getElementById('dimTextArea');
|
||||
if (dimTextArea) {
|
||||
|
|
@ -24,7 +24,7 @@ export function loadUIState(dock) {
|
|||
const state = JSON.parse(stateStr);
|
||||
|
||||
if (state.dockWidth) {
|
||||
dock.dockEl.css({width: state.dockWidth + 'px'});;
|
||||
dock.dockEl.style.width = state.dockWidth + 'px';
|
||||
}
|
||||
|
||||
const dimTextArea = document.getElementById('dimTextArea');
|
||||
|
|
@ -32,9 +32,9 @@ export function loadUIState(dock) {
|
|||
dimTextArea.style.height = state.dimTextAreaHeight + 'px';
|
||||
}
|
||||
|
||||
if (state.views) {
|
||||
dock.setState(state.views);
|
||||
}
|
||||
// if (state.views) {
|
||||
// dock.setState(state.views);
|
||||
// }
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import {Styles} from './styles';
|
||||
import {Bus, Parameters} from '../ui/toolkit';
|
||||
import {ParametricManager} from './parametric';
|
||||
import {HistoryManager} from './history';
|
||||
import {ToolManager} from './tools/manager';
|
||||
|
|
@ -25,15 +24,14 @@ class Viewer {
|
|||
// used to keep all internal data with such precision transforming the input from user
|
||||
this.presicion = 3;
|
||||
this.canvas = canvas;
|
||||
this.params = new Parameters();
|
||||
this.io = new IO(this);
|
||||
this.streams = sketcherStreams(this);
|
||||
var viewer = this;
|
||||
const viewer = this;
|
||||
this.retinaPxielRatio = window.devicePixelRatio > 1 ? window.devicePixelRatio : 1;
|
||||
|
||||
function updateCanvasSize() {
|
||||
var canvasWidth = canvas.parentNode.offsetWidth;
|
||||
var canvasHeight = canvas.parentNode.offsetHeight;
|
||||
const canvasWidth = canvas.parentNode.offsetWidth;
|
||||
const canvasHeight = canvas.parentNode.offsetHeight;
|
||||
|
||||
canvas.width = canvasWidth * viewer.retinaPxielRatio;
|
||||
canvas.height = canvasHeight * viewer.retinaPxielRatio;
|
||||
|
|
@ -54,7 +52,6 @@ class Viewer {
|
|||
set: viewer.setActiveLayer
|
||||
});
|
||||
|
||||
this.bus = new Bus();
|
||||
this.ctx = this.canvas.getContext("2d");
|
||||
this._activeLayer = null;
|
||||
this.layers = [
|
||||
|
|
@ -63,10 +60,7 @@ class Viewer {
|
|||
];
|
||||
this.dimLayer = this.createLayer("_dim", Styles.DIM);
|
||||
this.dimLayers = [this.dimLayer];
|
||||
this.bus.defineObservable(this, 'dimScale', 1);
|
||||
this.bus.subscribe('dimScale', function () {
|
||||
viewer.refresh();
|
||||
});
|
||||
this.streams.dimScale.attach(() => this.refresh());
|
||||
|
||||
this._workspace = [this.layers, this.dimLayers];
|
||||
|
||||
|
|
@ -90,6 +84,10 @@ class Viewer {
|
|||
this.refresh();
|
||||
}
|
||||
|
||||
get dimScale() {
|
||||
return this.streams.dimScale.value;
|
||||
}
|
||||
|
||||
get selected() {
|
||||
return this.captured.selection;
|
||||
}
|
||||
|
|
@ -304,10 +302,12 @@ class Viewer {
|
|||
showBounds(x1, y1, x2, y2, offset) {
|
||||
const dx = Math.max(x2 - x1, 1);
|
||||
const dy = Math.max(y2 - y1, 1);
|
||||
if (dx > dy) {
|
||||
this.scale = this.canvas.height / dx;
|
||||
const cRatio = this.canvas.width / this.canvas.height;
|
||||
|
||||
if (dy * cRatio >= dx) {
|
||||
this.scale = this.canvas.height / dy;
|
||||
} else {
|
||||
this.scale = this.canvas.width / dy;
|
||||
this.scale = this.canvas.width / dx;
|
||||
}
|
||||
this.translate.x = -x1 * this.scale;
|
||||
this.translate.y = -y1 * this.scale;
|
||||
|
|
@ -473,7 +473,6 @@ class Viewer {
|
|||
setActiveLayer(layer) {
|
||||
if (!layer.readOnly) {
|
||||
this._activeLayer = layer;
|
||||
this.bus.dispatch("activeLayer");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
export default function( map, block ) {
|
||||
let out = '';
|
||||
Object.keys( map ).map(function( prop ) {
|
||||
out += block.fn( {key: prop, value: map[ prop ]} );
|
||||
});
|
||||
return out;
|
||||
};
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
export function Terminal(win, commandProcessor, variantsSupplier) {
|
||||
this.win = win;
|
||||
this.out = win.root.find('.terminal-output');
|
||||
const input = win.root.find('.terminal-input input');
|
||||
this.input = input;
|
||||
|
||||
win.onShowCallback = function() {
|
||||
input.focus();
|
||||
};
|
||||
this.makeAlwaysFocusable();
|
||||
|
||||
this.history = [];
|
||||
this.historyPointer = 0;
|
||||
const setHistory = () => {
|
||||
if (this.history.length == 0) return;
|
||||
input.val(this.history[this.historyPointer]);
|
||||
};
|
||||
|
||||
input.keydown((e) => {
|
||||
function consumeEvent() {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
if (e.keyCode == 9) {
|
||||
const text = input.val();
|
||||
let variants = variantsSupplier().filter(v => v.startsWith(text));
|
||||
variants.sort();
|
||||
if (variants.length == 0) {
|
||||
} else {
|
||||
const shared = sharedStartOfSortedArray(variants);
|
||||
if (shared.length != text.length) {
|
||||
input.val(shared);
|
||||
} else {
|
||||
let autocompleteArea = this.out.find('.autocomplete-area');
|
||||
if (autocompleteArea.length == 0) {
|
||||
autocompleteArea = $('<div>', {'class': 'terminal-commandText autocomplete-area'});
|
||||
this.out.append(autocompleteArea);
|
||||
}
|
||||
let more = '';
|
||||
const limit = 20;
|
||||
if (variants.length > limit) {
|
||||
more = '... and ' + (variants.length - limit) + ' more';
|
||||
variants = variants.slice(0,limit);
|
||||
}
|
||||
autocompleteArea.text(variants.join(' ') + more);
|
||||
this.scrollToTheEnd();
|
||||
}
|
||||
}
|
||||
consumeEvent();
|
||||
} else if (e.keyCode == 38) {
|
||||
this.historyPointer = Math.max(this.historyPointer - 1, 0);
|
||||
setHistory();
|
||||
consumeEvent();
|
||||
} else if (e.keyCode == 40) {
|
||||
if (this.historyPointer != this.history.length) {
|
||||
this.historyPointer = Math.min(this.historyPointer + 1, this.history.length - 1);
|
||||
setHistory();
|
||||
}
|
||||
consumeEvent();
|
||||
}
|
||||
});
|
||||
|
||||
input.keyup((e) => {
|
||||
if(e.keyCode == 13) {
|
||||
const command = input.val();
|
||||
this.out.find('.autocomplete-area').remove();
|
||||
input.val('');
|
||||
this.out.append($('<div>', {text: '> '+command, 'class': 'terminal-commandText'}));
|
||||
if (command != null && command.trim().length != 0) {
|
||||
const result = commandProcessor(command);
|
||||
this.print(result);
|
||||
if (this.history.length == 0 || command != this.history[this.history.length - 1]) {
|
||||
this.history.push(command);
|
||||
}
|
||||
this.historyPointer = this.history.length;
|
||||
}
|
||||
this.scrollToTheEnd();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Terminal.prototype.makeAlwaysFocusable = function() {
|
||||
let wasMove = false;
|
||||
this.win.root.mousedown(() => {
|
||||
wasMove = false;
|
||||
return true;
|
||||
});
|
||||
this.win.root.mousemove(() => {
|
||||
wasMove = true;
|
||||
return true;
|
||||
});
|
||||
this.win.root.mouseup(() => {
|
||||
if (!wasMove) this.input.focus();
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
Terminal.prototype.scrollToTheEnd = function() {
|
||||
this.out.parent().scrollTop(this.out.height());
|
||||
};
|
||||
|
||||
Terminal.prototype.print = function(text) {
|
||||
this.out.append($('<div>', {text, 'class': 'terminal-commandResult'}));
|
||||
this.scrollToTheEnd();
|
||||
};
|
||||
|
||||
function sharedStartOfSortedArray(array){
|
||||
var a1= array[0], a2= array[array.length-1], L= a1.length, i= 0;
|
||||
while(i<L && a1.charAt(i)=== a2.charAt(i)) i++;
|
||||
return a1.substring(0, i);
|
||||
}
|
||||
|
|
@ -1,341 +0,0 @@
|
|||
|
||||
export function add(parent, child) {
|
||||
parent.content.append(child.root);
|
||||
}
|
||||
|
||||
export function methodRef(_this, methodName, args) {
|
||||
return function() {
|
||||
_this[methodName].apply(_this, args);
|
||||
};
|
||||
}
|
||||
|
||||
export function Box(parent) {
|
||||
this.root = this.content = $('<div class="tc-box" />');
|
||||
this.root.addClass('tc-box tc-scroll');
|
||||
this.root.appendTo(parent ? parent : 'body');
|
||||
}
|
||||
|
||||
Box.prototype.close = function() {
|
||||
this.root.remove();
|
||||
};
|
||||
|
||||
export function Panel() {
|
||||
this.root = this.content = $('<div />');
|
||||
this.root.addClass('tc-panel tc-scroll');
|
||||
}
|
||||
|
||||
Panel.prototype.close = function() {
|
||||
this.root.remove();
|
||||
};
|
||||
|
||||
export function Folder(title) {
|
||||
this.root = $('<div/>', {'class': 'tc-folder'});
|
||||
this.content = $('<div/>');
|
||||
this.root.append($('<div/>', {text: title, 'class': 'tc-row tc-title'}));
|
||||
this.root.append(this.content);
|
||||
}
|
||||
|
||||
export function Button(title) {
|
||||
this.root = $('<div/>',
|
||||
{'class': 'tc-row tc-ctrl tc-ctrl-btn', text: title});
|
||||
}
|
||||
|
||||
export function CheckBox(title, checked) {
|
||||
this.root = $('<div/>',
|
||||
{'class': 'tc-row tc-ctrl'});
|
||||
this.root.append('<label><input type="checkbox">' + title + '</label>')
|
||||
this.input = this.root.find("input");
|
||||
this.input.prop('checked', !!checked);
|
||||
}
|
||||
|
||||
export function InlineRadio(choiceLabels, choiceValues, checkedIndex) {
|
||||
var name = 'TCAD.toolkit.InlineRadio_' + (InlineRadio.COUNTER++)
|
||||
this.root = $('<div/>',
|
||||
{'class': 'tc-row tc-ctrl tc-inline-radio'});
|
||||
this.inputs = [];
|
||||
for (var i = 0; i < choiceLabels.length; i++) {
|
||||
var checked = checkedIndex === i ? "checked" : '';
|
||||
var label = $('<label><input type="radio" name="' + name + '" value="' + choiceValues[i] + '"><span>' + choiceLabels[i] + '</span></label>');
|
||||
this.inputs.push(label.find("input"));
|
||||
this.root.append(label);
|
||||
}
|
||||
this.inputs[checkedIndex].prop('checked', true);
|
||||
}
|
||||
|
||||
InlineRadio.prototype.getValue = function() {
|
||||
for (var i = 0; i < this.inputs.length; i++) {
|
||||
if (this.inputs[i].prop('checked')) {
|
||||
return this.inputs[i].attr('value');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
InlineRadio.prototype.setValue = function(v) {
|
||||
this.root.find('input[value='+v+']').prop('checked', true);
|
||||
};
|
||||
|
||||
|
||||
InlineRadio.COUNTER = 0;
|
||||
|
||||
export function propLayout(root, name, valueEl) {
|
||||
root.append($('<span/>', {'class': 'tc-prop-name', text: name}))
|
||||
.append($('<div/>', {'class': 'tc-prop-value'})
|
||||
.append(valueEl));
|
||||
}
|
||||
|
||||
function NumberWidget(name, initValue, baseStep, round) {
|
||||
this.root = $('<div/>', {'class': 'tc-row tc-ctrl tc-ctrl-number'});
|
||||
this.input = $("<input type='text' value='"+initValue+"' />");
|
||||
this.slide = false;
|
||||
baseStep = baseStep || 1;
|
||||
round = round || 0;
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
this.accelerator = 100;
|
||||
var scope = this;
|
||||
var lastValue = null;
|
||||
function trigger() {
|
||||
if ($(this).val() !== lastValue) {
|
||||
$(this).trigger('t-change');
|
||||
lastValue = $(this).val();
|
||||
}
|
||||
}
|
||||
|
||||
this.input.on('input', function(e) {
|
||||
var val = $(this).val();
|
||||
//var floatRegex = /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/;
|
||||
//if (!floatRegex.test(val)) {
|
||||
// $(this).val(val.replace(/[^0-9\.-]/g, ''));
|
||||
//}
|
||||
trigger.call(this);
|
||||
});
|
||||
this.input.get(0).addEventListener('mousewheel', function (e) {
|
||||
var delta = 0;
|
||||
if ( e.wheelDelta ) { // WebKit / Opera / Explorer 9
|
||||
delta = e.wheelDelta;
|
||||
} else if ( e.detail ) { // Firefox
|
||||
delta = - e.detail;
|
||||
}
|
||||
var val = $(this).val();
|
||||
if (!val) val = 0;
|
||||
var step = baseStep * (e.shiftKey ? scope.accelerator : 1);
|
||||
val = parseFloat(val) + (delta < 0 ? -step : step);
|
||||
if (scope.min != null && val < scope.min) {
|
||||
val = scope.min;
|
||||
}
|
||||
if (scope.max != null && val > scope.max) {
|
||||
val = scope.max;
|
||||
}
|
||||
if (round !== 0) {
|
||||
val = val.toFixed(round);
|
||||
}
|
||||
$(this).val(val);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
trigger.call(this);
|
||||
}, false);
|
||||
propLayout(this.root, name, this.input);
|
||||
}
|
||||
|
||||
NumberWidget.prototype.val = function() {
|
||||
return Number(this.input.val());
|
||||
};
|
||||
|
||||
export function Text(name, initValue) {
|
||||
this.root = $('<div/>', {'class': 'tc-row tc-ctrl'});
|
||||
this.input = $("<input type='text' value='"+initValue+"' />");
|
||||
propLayout(this.root, name, this.input);
|
||||
}
|
||||
|
||||
export function Combo(id, labelText) {
|
||||
this.root = $('<div/>', {'class': 'tc-row tc-ctrl tc-ctrl-combo'});
|
||||
var label = $('<span/>', {'class': 'tc-prop-name', text: labelText});
|
||||
this.select = $('<select>', {id : id});
|
||||
this.root.append(label)
|
||||
.append($('<div/>', {'class': 'tc-prop-value'}).append(this.select));
|
||||
}
|
||||
|
||||
export function ButtonRow(captions, actions) {
|
||||
|
||||
this.root = $('<div/>',
|
||||
{'class': 'tc-row tc-ctrl tc-buttons-block'});
|
||||
|
||||
function withAction(btn, action) {
|
||||
return btn.click(function(){
|
||||
action.call()
|
||||
});
|
||||
}
|
||||
for (var i = 0; i < captions.length; i++) {
|
||||
var caption = captions[i];
|
||||
var btn = $('<span/>', {
|
||||
text: caption,
|
||||
'class': 'tc-block-btn active-btn'
|
||||
});
|
||||
withAction(btn, actions[i]);
|
||||
this.root.append(btn);
|
||||
}
|
||||
}
|
||||
|
||||
export function List() {
|
||||
this.root = $('<div/>', {'class': 'tc-list'});
|
||||
}
|
||||
|
||||
List.prototype.addRow = function(name) {
|
||||
var row = $('<div/>', {
|
||||
text: name, 'class': 'tc-row tc-pseudo-btn'
|
||||
});
|
||||
this.root.append(row);
|
||||
return row;
|
||||
};
|
||||
|
||||
List.setIconForRow = function(row, icon) {
|
||||
row.css({
|
||||
'background-image' : 'url('+icon+')'
|
||||
});
|
||||
};
|
||||
|
||||
export function Tree() {
|
||||
this.root = $('<div/>', {'class': 'tc-tree'});
|
||||
}
|
||||
|
||||
Tree.prototype.set = function(data) {
|
||||
this.root.empty();
|
||||
this._fill(data, 0);
|
||||
};
|
||||
|
||||
Tree.prototype._fill = function(data, level) {
|
||||
var notLeaf = data.children !== undefined && data.children.length !== 0;
|
||||
if (data.name !== undefined) {
|
||||
this.root.append($('<div/>', {
|
||||
text: data.name, 'class': 'tc-row' + (notLeaf ? ' tc-chevron-open' : ''),
|
||||
css: {'margin-left': level * (notLeaf ? 10 : 16) + 'px'}
|
||||
}));
|
||||
}
|
||||
if (notLeaf) {
|
||||
for (var i = 0; i < data.children.length; i++) {
|
||||
var child = data.children[i];
|
||||
this._fill(child, level + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function Parameters() {
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
Parameters.prototype.define = function(name, initValue) {
|
||||
function fn(name) {
|
||||
return '___' + name;
|
||||
}
|
||||
this[fn(name)] = initValue;
|
||||
return Object.defineProperty(this, name, {
|
||||
get: function() { return this[fn(name)]},
|
||||
set: function(value) {
|
||||
var oldValue = this[fn(name)];
|
||||
this[fn(name)] = value;
|
||||
this.notify(name, value, oldValue);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Parameters.prototype.subscribe = function(name, listenerId, callback, scope) {
|
||||
var listenerList = this.listeners[name];
|
||||
if (listenerList === undefined) {
|
||||
listenerList = [];
|
||||
this.listeners[name] = listenerList;
|
||||
}
|
||||
var callbackFunc = scope === undefined ? callback : function() {
|
||||
callback.apply(scope, arguments);
|
||||
};
|
||||
listenerList.push([listenerId, callbackFunc]);
|
||||
var params = this;
|
||||
return (function () { callbackFunc(params[name], undefined, null) }); // return init function
|
||||
};
|
||||
|
||||
Parameters.prototype.notify = function(name, newValue, oldValue) {
|
||||
var listenerList = this.listeners[name];
|
||||
if (listenerList !== undefined) {
|
||||
for (var i = 0; i < listenerList.length; i++) {
|
||||
var listenerId = listenerList[i][0];
|
||||
var callback = listenerList[i][1];
|
||||
if (listenerId == null || this.__currentSender == null || listenerId != this.__currentSender) {
|
||||
callback(newValue, oldValue, this.__currentSender);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.__currentSender = null;
|
||||
};
|
||||
|
||||
Parameters.prototype.set = function(name, value, sender) {
|
||||
this.__currentSender = sender;
|
||||
this[name] = value;
|
||||
};
|
||||
|
||||
export function Bus() {
|
||||
this.listeners = {};
|
||||
}
|
||||
|
||||
Bus.prototype.subscribe = function(event, callback, listenerId) {
|
||||
var listenerList = this.listeners[event];
|
||||
if (listenerList === undefined) {
|
||||
listenerList = [];
|
||||
this.listeners[event] = listenerList;
|
||||
}
|
||||
if (listenerId == undefined) listenerId = null;
|
||||
listenerList.push([callback, listenerId]);
|
||||
return callback;
|
||||
};
|
||||
|
||||
Bus.prototype.unsubscribe = function(event, callback) {
|
||||
const listenerList = this.listeners[event];
|
||||
for (let i = 0; i < listenerList.length; i++) {
|
||||
if (listenerList[i][0] === callback) {
|
||||
listenerList.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bus.prototype.dispatch = function(event, data, sender) {
|
||||
var listenerList = this.listeners[event];
|
||||
if (listenerList !== undefined) {
|
||||
for (var i = 0; i < listenerList.length; i++) {
|
||||
const callback = listenerList[i][0];
|
||||
const listenerId = listenerList[i][1];
|
||||
if (sender == undefined || listenerId == null || listenerId != sender) {
|
||||
try {
|
||||
callback(data);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Bus.Observable = function(initValue) {
|
||||
this.value = initValue;
|
||||
};
|
||||
|
||||
Bus.prototype.defineObservable = function(scope, name, initValue, eventName) {
|
||||
if (eventName == undefined) eventName = name;
|
||||
var observable = new Bus.Observable(initValue);
|
||||
var bus = this;
|
||||
return Object.defineProperty(scope, name, {
|
||||
get: function() { return observable.value;},
|
||||
set: function(value) {
|
||||
observable.value = value;
|
||||
bus.dispatch(eventName, value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function config(obj, props) {
|
||||
for (var key in props) {
|
||||
obj[key] = props[key];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
export {NumberWidget as Number}
|
||||
404
web/app/ui/ui.js
404
web/app/ui/ui.js
|
|
@ -1,404 +0,0 @@
|
|||
|
||||
/** @constructor */
|
||||
function Window(el, winManager) {
|
||||
this.root = el;
|
||||
this.neverOpened = !this.root.is(':visible');
|
||||
this.tileUpRelative = $('body');
|
||||
this.onShowCallback = null;
|
||||
var root = this.root;
|
||||
var caption = this.root.find('.tool-caption');
|
||||
caption.each(function() {
|
||||
var closeBtn = '<span class="btn rm" style="float: right;"><i class="fa fa-remove"></i></span>';
|
||||
$(this).append(closeBtn);
|
||||
});
|
||||
this.root.find('.tool-caption .rm').click(function() {
|
||||
root.hide();
|
||||
});
|
||||
var DIRS = DIRECTIONS;
|
||||
winManager.registerResize(this.root, DIRS.NORTH | DIRS.SOUTH | DIRS.WEST | DIRS.EAST);
|
||||
winManager.registerDrag(this.root, caption);
|
||||
}
|
||||
|
||||
|
||||
Window.prototype.show = function() {
|
||||
this.root.show();
|
||||
}
|
||||
|
||||
Window.prototype.toggle = function() {
|
||||
var aboutToShow = !this.root.is(':visible');
|
||||
if (aboutToShow) {
|
||||
this.tileUpPolicy(this.neverOpened, this.tileUpRelative);
|
||||
}
|
||||
this.neverOpened = false ;
|
||||
this.root.toggle();
|
||||
if (aboutToShow && this.onShowCallback != null) {
|
||||
this.onShowCallback(this);
|
||||
}
|
||||
};
|
||||
|
||||
Window.prototype.tileUpPolicy = function(firstTime, relativeEl) {
|
||||
var span = 20;
|
||||
var relOff = relativeEl.offset();
|
||||
var off = this.root.offset();
|
||||
off = {
|
||||
left: parseInt(this.root.css('left')),
|
||||
top: parseInt(this.root.css('top'))
|
||||
};
|
||||
|
||||
if (firstTime) {
|
||||
off = {
|
||||
left: relOff.left + relativeEl.width() - this.root.width() - span,
|
||||
top: relOff.top + relativeEl.height() - this.root.height() - span
|
||||
};
|
||||
this.root.css({
|
||||
left: off.left + 'px',
|
||||
top: off.top + 'px'
|
||||
});
|
||||
}
|
||||
var needToSet = false;
|
||||
if (off.left < relOff.left || off.left >= relOff.left + relativeEl.width() - span) {
|
||||
off.left = relOff.left + span;
|
||||
needToSet = true;
|
||||
}
|
||||
if (off.top < relOff.top || off.top >= relOff.top + relativeEl.height() - span) {
|
||||
off.top = relOff.top + span;
|
||||
needToSet = true;
|
||||
}
|
||||
if (needToSet) {
|
||||
this.root.css({
|
||||
left: off.left + 'px',
|
||||
top: off.top + 'px'
|
||||
});
|
||||
}
|
||||
//var fixedWidth = null;
|
||||
//var fixedHeight = null;
|
||||
//
|
||||
//if (off.left + this.root.width() > relOff.left + relativeEl.width()) {
|
||||
// fixedWidth = this.root.width() - span * 2;
|
||||
//}
|
||||
//if (off.top + this.root.height() > relOff.top + relativeEl.height()) {
|
||||
// fixedHeight = this.root.width() - span * 2;
|
||||
//}
|
||||
//if (fixedWidth != null) {
|
||||
// console.log(fixedWidth)
|
||||
// this.root.css({ width : fixedWidth + 'px'});
|
||||
//}
|
||||
//if (fixedHeight != null) {
|
||||
// this.root.css({ height : fixedHeight + 'px'});
|
||||
//}
|
||||
};
|
||||
|
||||
function WinManager() {
|
||||
this.moveHandler = null;
|
||||
var wm = this;
|
||||
document.body.addEventListener("mousemove", function( e ) {
|
||||
if (wm.moveHandler != null) {
|
||||
wm.moveHandler(e);
|
||||
e.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
document.body.addEventListener("mouseup", function( e ) {
|
||||
wm.moveHandler = null;
|
||||
}, false);
|
||||
}
|
||||
|
||||
WinManager.prototype.captureDrag = function(el, e) {
|
||||
var origin = {x : e.pageX, y : e.pageY};
|
||||
var originLocation = el.offset();
|
||||
this.moveHandler = function(e) {
|
||||
var dx = e.pageX - origin.x;
|
||||
var dy = e.pageY - origin.y;
|
||||
el.offset({left : originLocation.left + dx, top : originLocation.top + dy});
|
||||
};
|
||||
};
|
||||
|
||||
WinManager.prototype.captureResize = function(el, dirMask, e, onResize) {
|
||||
|
||||
var origin = {x : e.pageX, y : e.pageY};
|
||||
var originSize = {x : el.outerWidth(), y : el.height()};
|
||||
var originLocation = el.offset();
|
||||
var north = _maskTest(dirMask, DIRECTIONS.NORTH);
|
||||
var south = _maskTest(dirMask, DIRECTIONS.SOUTH);
|
||||
var west = _maskTest(dirMask, DIRECTIONS.WEST);
|
||||
var east = _maskTest(dirMask, DIRECTIONS.EAST);
|
||||
|
||||
this.moveHandler = function(e) {
|
||||
var dx = e.pageX - origin.x;
|
||||
var dy = e.pageY - origin.y;
|
||||
if (east) {
|
||||
el.css('width', Math.round(originSize.x + dx) + 'px');
|
||||
}
|
||||
var top = originLocation.top;
|
||||
var left = originLocation.left;
|
||||
var setLoc = false;
|
||||
if (west) {
|
||||
el.css('width', Math.round(originSize.x - dx) + 'px');
|
||||
left += dx;
|
||||
setLoc = true;
|
||||
}
|
||||
if (south) {
|
||||
el.css('height', Math.round(originSize.y + dy) + 'px');
|
||||
}
|
||||
if (north) {
|
||||
el.css('height', Math.round(originSize.y - dy) + 'px');
|
||||
top += dy;
|
||||
setLoc = true;
|
||||
}
|
||||
if (setLoc) {
|
||||
el.offset({left : left, top: top});
|
||||
}
|
||||
if (onResize !== undefined) {
|
||||
onResize();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var DIRECTIONS = {
|
||||
NORTH : 0x0001,
|
||||
SOUTH : 0x0010,
|
||||
WEST : 0x0100,
|
||||
EAST : 0x1000,
|
||||
};
|
||||
|
||||
WinManager.prototype.registerResize = function(el, dirMask, onResize) {
|
||||
var wm = this;
|
||||
var north = _maskTest(dirMask, DIRECTIONS.NORTH);
|
||||
var south = _maskTest(dirMask, DIRECTIONS.SOUTH);
|
||||
var west = _maskTest(dirMask, DIRECTIONS.WEST);
|
||||
var east = _maskTest(dirMask, DIRECTIONS.EAST);
|
||||
|
||||
var borderTop = parseInt(el.css('borderTopWidth'), 10);
|
||||
var borderLeft = parseInt(el.css('borderLeftWidth'), 10);
|
||||
|
||||
function onNorthEdge(e, el) {
|
||||
var offset = el.offset();
|
||||
return e.pageY < offset.top + borderTop;
|
||||
}
|
||||
|
||||
function onSouthEdge(e, el) {
|
||||
var offset = el.offset();
|
||||
var height = el.height();
|
||||
return e.pageY > offset.top + height + borderTop;
|
||||
}
|
||||
|
||||
function onWestEdge(e, el) {
|
||||
var offset = el.offset();
|
||||
return e.pageX < offset.left + borderLeft;
|
||||
}
|
||||
|
||||
function onEastEdge(e, el) {
|
||||
var offset = el.offset();
|
||||
var width = el.width();
|
||||
return e.pageX > offset.left + width + borderLeft;
|
||||
}
|
||||
|
||||
|
||||
el.mousedown(function(e) {
|
||||
var $this = $(this);
|
||||
if (north && east && onNorthEdge(e, $this) && onEastEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.NORTH | DIRECTIONS.EAST, e, onResize);
|
||||
} else if (north && west && onNorthEdge(e, $this) && onWestEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.NORTH | DIRECTIONS.WEST, e, onResize);
|
||||
} else if (south && east && onSouthEdge(e, $this) && onEastEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.SOUTH | DIRECTIONS.EAST, e, onResize);
|
||||
} else if (south && west && onSouthEdge(e, $this) && onWestEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.SOUTH | DIRECTIONS.WEST, e, onResize);
|
||||
} else if (north && onNorthEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.NORTH, e, onResize);
|
||||
} else if (south && onSouthEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.SOUTH, e, onResize);
|
||||
} else if (west && onWestEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.WEST, e, onResize);
|
||||
} else if (east && onEastEdge(e, $this)) {
|
||||
wm.captureResize(el, DIRECTIONS.EAST, e, onResize);
|
||||
}
|
||||
});
|
||||
el.mousemove(function(e) {
|
||||
|
||||
var $this = $(this);
|
||||
if (north && east && onNorthEdge(e, $this) && onEastEdge(e, $this)) {
|
||||
el.css('cursor', 'nesw-resize');
|
||||
} else if (north && west && onNorthEdge(e, $this) && onWestEdge(e, $this)) {
|
||||
el.css('cursor', 'nwse-resize');
|
||||
} else if (south && east && onSouthEdge(e, $this) && onEastEdge(e, $this)) {
|
||||
el.css('cursor', 'nwse-resize');
|
||||
} else if (south && west && onSouthEdge(e, $this) && onWestEdge(e, $this)) {
|
||||
el.css('cursor', 'nesw-resize');
|
||||
} else if (south && onSouthEdge(e, $this)) {
|
||||
el.css('cursor', 'ns-resize');
|
||||
} else if (north && onNorthEdge(e, $this)) {
|
||||
el.css('cursor', 'ns-resize');
|
||||
} else if (east && onEastEdge(e, $this)) {
|
||||
el.css('cursor', 'ew-resize');
|
||||
} else if (west && onWestEdge(e, $this)) {
|
||||
el.css('cursor', 'ew-resize');
|
||||
} else {
|
||||
el.css('cursor', "");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
WinManager.prototype.registerDrag = function(el, dragger) {
|
||||
var wm = this;
|
||||
dragger.mousedown(function(e) {
|
||||
wm.captureDrag(el, e);
|
||||
});
|
||||
};
|
||||
|
||||
function bindOpening(btn, win) {
|
||||
btn.click(function(e) {
|
||||
openWin(win, e);
|
||||
});
|
||||
}
|
||||
|
||||
function createActionsWinBuilder(win) {
|
||||
var content = win.root.find('.content');
|
||||
var template = content.html();
|
||||
content.empty();
|
||||
return function(name, action) {
|
||||
content.append(template.replace("$value$", name));
|
||||
content.find('div:last input').click(action);
|
||||
};
|
||||
}
|
||||
|
||||
function closeWin(win) {
|
||||
win.root.hide();
|
||||
}
|
||||
|
||||
function openWin(win, mouseEvent) {
|
||||
|
||||
var x = mouseEvent.pageX;
|
||||
var y = mouseEvent.pageY;
|
||||
var pageW = $(window).width();
|
||||
var pageH = $(window).height();
|
||||
var winW = win.root.width();
|
||||
var winH = win.root.height();
|
||||
|
||||
var left = x < pageW / 2 ? x : x - winW;
|
||||
var top = y < pageH / 2 ? y : y - winH;
|
||||
|
||||
win.root.show();
|
||||
win.root.offset({top : top, left : left});
|
||||
|
||||
}
|
||||
|
||||
/** @constructor */
|
||||
function List(id, model) {
|
||||
this.ul = $('<ul>', { 'class' : 'tlist', id : id});
|
||||
this.model = model;
|
||||
this.template = '<li>$name$<span class="btn rm" style="float: right;"><i class="fa fa-remove"></i></span></li>';
|
||||
}
|
||||
|
||||
List.prototype.refresh = function() {
|
||||
this.ul.empty();
|
||||
var items = this.model.items();
|
||||
var model = this.model;
|
||||
function makeCallbacks(li, item, index) {
|
||||
li.find('.rm').click(function(e) {
|
||||
model.remove(item, index);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
li.hover(function() {model.hover(item, index)});
|
||||
li.mouseleave(function() {model.mouseleave(item, index)});
|
||||
li.click(function() {model.click(item, index)});
|
||||
}
|
||||
|
||||
|
||||
for (var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
var li = $(this.template.replace('$name$', item.name));
|
||||
this.ul.append(li);
|
||||
makeCallbacks(li, item, i)
|
||||
}
|
||||
};
|
||||
|
||||
function dockBtn(name, icon) {
|
||||
var btn = $('<span>', {'class': 'dock-btn'});
|
||||
btn.append(faBtn(icon));
|
||||
btn.append($('<span>', {'class': 'txt'}).text(name));
|
||||
return btn;
|
||||
}
|
||||
|
||||
function faBtn (iconName) {
|
||||
return $('<i>', {'class' : 'fa fa-'+iconName});
|
||||
}
|
||||
|
||||
function Dock(dockEl, switcherEl, viewDefinitions) {
|
||||
this.views = {};
|
||||
this.dockEl = dockEl;
|
||||
function bindClick(dock, switchEl, viewName) {
|
||||
switchEl.click(function(e) {
|
||||
if (dock.isVisible(viewName)) {
|
||||
dock.hide(viewName);
|
||||
} else {
|
||||
dock.show(viewName);
|
||||
}
|
||||
});
|
||||
}
|
||||
for (var i = 0; i < viewDefinitions.length; i++) {
|
||||
var viewDef = viewDefinitions[i];
|
||||
var view = {};
|
||||
this.views[viewDef.name] = view;
|
||||
view.node = $('<div>', {'class': 'dock-node'});
|
||||
var caption = $('<div>', {'class': 'tool-caption'});
|
||||
caption.append($('<span>', {'class': 'txt'}).text(viewDef.name.toUpperCase()));
|
||||
caption.append(faBtn(viewDef.icon));
|
||||
view.node.append(caption);
|
||||
view.node.hide();
|
||||
this.dockEl.append(view.node);
|
||||
view.switchBtn = dockBtn(viewDef.name, viewDef.icon);
|
||||
bindClick(this, view.switchBtn, viewDef.name);
|
||||
switcherEl.append(view.switchBtn);
|
||||
}
|
||||
}
|
||||
|
||||
Dock.prototype.show = function(viewName) {
|
||||
var view = this.views[viewName];
|
||||
if (view.switchBtn.hasClass('selected')) {
|
||||
return;
|
||||
}
|
||||
if (!this.dockEl.is(":visible")) {
|
||||
this.dockEl.show();
|
||||
$('body').trigger('layout');
|
||||
}
|
||||
view.node.show();
|
||||
view.switchBtn.addClass('selected');
|
||||
};
|
||||
|
||||
Dock.prototype.hide = function(viewName) {
|
||||
var view = this.views[viewName];
|
||||
if (!view.switchBtn.hasClass('selected')) {
|
||||
return;
|
||||
}
|
||||
view.node.hide();
|
||||
view.switchBtn.removeClass('selected');
|
||||
if (this.dockEl.find('.dock-node:visible').length == 0) {
|
||||
this.dockEl.hide();
|
||||
$('body').trigger('layout');
|
||||
}
|
||||
};
|
||||
|
||||
Dock.prototype.isVisible = function(viewName) {
|
||||
return this.views[viewName].switchBtn.hasClass('selected');
|
||||
};
|
||||
|
||||
Dock.prototype.setState = function(state) {
|
||||
state.forEach(viewName => this.show(viewName));
|
||||
};
|
||||
|
||||
Dock.prototype.getState = function() {
|
||||
const state = [];
|
||||
Object.keys(this.views).forEach(viewName => {
|
||||
if (this.isVisible(viewName)) {
|
||||
state.push(viewName);
|
||||
}
|
||||
});
|
||||
return state;
|
||||
};
|
||||
|
||||
function _maskTest(mask, value) {
|
||||
return (mask & value) === value;
|
||||
}
|
||||
|
||||
export { WinManager, Window, List, Dock, dockBtn, faBtn, openWin, closeWin, bindOpening, createActionsWinBuilder, DIRECTIONS };
|
||||
23
web/app/utils/domUtils.js
Normal file
23
web/app/utils/domUtils.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
export function createElement(type, id, className, text) {
|
||||
const el = document.createElement(type);
|
||||
if (id) {
|
||||
el.id = id;
|
||||
}
|
||||
if (className) {
|
||||
el.className = className;
|
||||
}
|
||||
if (text) {
|
||||
el.innerText = text;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
export function select(query) {
|
||||
return document.querySelector(query)
|
||||
}
|
||||
|
||||
export function selectAll(query) {
|
||||
return document.querySelectorAll(query)
|
||||
}
|
||||
|
||||
6
web/app/utils/jqueryfy.js
vendored
6
web/app/utils/jqueryfy.js
vendored
|
|
@ -1,6 +0,0 @@
|
|||
import $ from 'jquery'
|
||||
|
||||
// Usage of jquery is absolutely deprecated. It exists only to support legacy code(2d sketcher and test framework) and
|
||||
// will be gone soon after complete transition to React. Main application never uses jquery.
|
||||
|
||||
window.jQuery = window.$ = $;
|
||||
|
|
@ -18,10 +18,6 @@ html, body {
|
|||
font-family: 'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.mono {
|
||||
font-family: Monaco, monospace;
|
||||
}
|
||||
|
||||
.logo {
|
||||
color: #bbb;
|
||||
font-size: 16px;
|
||||
|
|
@ -275,62 +271,6 @@ html, body {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
#commands {
|
||||
.mono;
|
||||
}
|
||||
|
||||
#commands .content {
|
||||
color: #C4E1A4;
|
||||
font-size: 11px
|
||||
}
|
||||
|
||||
.terminal-output-area {
|
||||
height: ~"calc(100% - 30px)"; //escape less processing
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.terminal-output {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.terminal-pusher {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.terminal-input {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.terminal-input input {
|
||||
color: #C4E1A4;
|
||||
background: inherit;
|
||||
outline: none;
|
||||
border: 0;
|
||||
margin-top: 4px;
|
||||
padding: 3px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.terminal-input input::-webkit-input-placeholder {
|
||||
color: #777777;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.terminal-commandText {
|
||||
color: #777777;
|
||||
}
|
||||
|
||||
.autocomplete-area {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
input[type=checkbox], input[type=radio] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#status {
|
||||
.helvetica;
|
||||
color: #fff;
|
||||
|
|
@ -357,3 +297,11 @@ input[type=checkbox], input[type=radio] {
|
|||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#top-toolbar:empty {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#right-toolbar:empty {
|
||||
width: 50px;
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@
|
|||
<body>
|
||||
<a id="downloader" style="display: none;" ></a>
|
||||
<div class="panel b-bot" style="width: 100%; display: flex; justify-content: space-between">
|
||||
<span class="logo" style="float:left">sketcher <sup> 2D</sup></span>
|
||||
<div style="display: flex" id="top-toolbar"></div>
|
||||
<span class="logo" style="float:left">sketcher <sup> 2D</sup></span>
|
||||
<div style="display: flex" id="top-toolbar"></div>
|
||||
</div>
|
||||
<div style="width: 100%; height: calc(100% - 65px); display: flex;">
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import suites from './suites'
|
|||
import {Menu} from './menu'
|
||||
import {TestEnv} from './test'
|
||||
import DurationFormat from './utils/duration-format'
|
||||
import $ from "jquery";
|
||||
|
||||
window.jQuery = window.$ = $;
|
||||
|
||||
$(() => {
|
||||
$(document).on('click', '.action-item', (e) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue