mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-09 09:52:34 +01:00
change the main toolbar in the sketcher mode
This commit is contained in:
parent
2941687dbc
commit
1b766da2d4
44 changed files with 427 additions and 311 deletions
|
|
@ -90,4 +90,11 @@ export function indexById(array) {
|
|||
return out;
|
||||
}
|
||||
|
||||
export function insertAfter(arr, item, toAdd) {
|
||||
const index = arr.indexOf(item);
|
||||
if (index !== -1) {
|
||||
arr.splice(index+1, 0, toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
export const EMPTY_ARRAY = Object.freeze([]);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ export class CombineStream extends StreamBase {
|
|||
|
||||
constructor(streams) {
|
||||
super();
|
||||
streams.forEach(stream => {
|
||||
if (!stream) {
|
||||
throw 'stream is undefined';
|
||||
}
|
||||
});
|
||||
this.streams = streams;
|
||||
this.values = this.streams.map(() => NOT_INITIALIZED);
|
||||
this.ready = false;
|
||||
|
|
|
|||
|
|
@ -20,4 +20,14 @@ export function ToolbarButton({children, disabled, ...props}) {
|
|||
|
||||
export function ToolbarSplitter() {
|
||||
return <div className={ls.splitter} />
|
||||
}
|
||||
|
||||
export function ToolbarBraker() {
|
||||
return <div className={ls.braker} />
|
||||
}
|
||||
|
||||
export function ToolbarGroup({children}) {
|
||||
return <div className={ls.group} >
|
||||
{children}
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
&.flat {
|
||||
border-radius: 0;
|
||||
}
|
||||
overflow: auto;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
|
|
@ -21,7 +20,8 @@
|
|||
text-align: center;
|
||||
white-space: nowrap;
|
||||
font-size: 10px;
|
||||
padding: 3px 7px;
|
||||
padding: 2px 2px;
|
||||
margin: 1px 5px;
|
||||
color: #555;
|
||||
pointer-events: auto;
|
||||
&:hover {
|
||||
|
|
@ -57,5 +57,8 @@
|
|||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
6
package-lock.json
generated
6
package-lock.json
generated
|
|
@ -9301,9 +9301,9 @@
|
|||
}
|
||||
},
|
||||
"react-icons": {
|
||||
"version": "3.9.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.9.0.tgz",
|
||||
"integrity": "sha512-gKbYKR+4QsD3PmIHLAM9TDDpnaTsr3XZeK1NTAb6WQQ+gxEdJ0xuCgLq0pxXdS7Utg2AIpcVhM1ut/jlDhcyNg==",
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.10.0.tgz",
|
||||
"integrity": "sha512-WsQ5n1JToG9VixWilSo1bHv842Cj5aZqTGiS3Ud47myF6aK7S/IUY2+dHcBdmkQcCFRuHsJ9OMUI0kTDfjyZXQ==",
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@
|
|||
"prop-types": "15.6.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-icons": "3.9.0",
|
||||
"react-icons": "^3.10.0",
|
||||
"react-toastify": "^5.5.0",
|
||||
"sprintf": "0.1.5",
|
||||
"three": "0.89.0"
|
||||
|
|
|
|||
41
web/app/cad/actions/ActionButtonBehavior.jsx
Normal file
41
web/app/cad/actions/ActionButtonBehavior.jsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import React, {useContext} from 'react';
|
||||
import {AppContext} from "../dom/components/AppContext";
|
||||
|
||||
export function ActionButtonBehavior({children, actionId}) {
|
||||
|
||||
const ctx = useContext(AppContext);
|
||||
|
||||
const request = {actionId, x: 0, y: 0};
|
||||
|
||||
let canceled = true;
|
||||
let shown = false;
|
||||
|
||||
function updateCoords({pageX, pageY}) {
|
||||
request.x = pageX + 10;
|
||||
request.y = pageY + 10;
|
||||
}
|
||||
|
||||
const actionService = ctx.services.action;
|
||||
|
||||
return children({
|
||||
onClick: e => actionService.run(actionId, e),
|
||||
onMouseEnter: e => {
|
||||
updateCoords(e);
|
||||
canceled = false;
|
||||
shown = false;
|
||||
setTimeout(() => {
|
||||
if (!canceled) {
|
||||
shown = true;
|
||||
actionService.showHintFor(request)
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
onMouseMove: updateCoords,
|
||||
onMouseLeave: () => {
|
||||
canceled = true;
|
||||
if (shown) {
|
||||
actionService.showHintFor(null)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
|
||||
export function mapActionBehavior(actionIdGetter) {
|
||||
return ({services}, props) => {
|
||||
const actionId = typeof actionIdGetter === 'string' ? actionIdGetter : actionIdGetter(props);
|
||||
|
||||
let request = {actionId, x:0, y:0};
|
||||
let canceled = true;
|
||||
let shown = false;
|
||||
|
||||
function updateCoords({pageX, pageY}) {
|
||||
request.x = pageX + 10;
|
||||
request.y = pageY + 10;
|
||||
}
|
||||
|
||||
return {
|
||||
onClick: e => services.action.run(actionId, e),
|
||||
onMouseEnter: e => {
|
||||
updateCoords(e);
|
||||
canceled = false;
|
||||
shown = false;
|
||||
setTimeout(() => {
|
||||
if (!canceled) {
|
||||
shown = true;
|
||||
services.action.showHintFor(request)
|
||||
}
|
||||
}, 500);
|
||||
},
|
||||
onMouseMove: updateCoords,
|
||||
onMouseLeave: () => {
|
||||
canceled = true;
|
||||
if (shown) {
|
||||
services.action.showHintFor(null)
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
import {mapActionBehavior} from './actionButtonBehavior';
|
||||
import mapContext from '../../../../modules/ui/mapContext';
|
||||
import {ActionButtonBehavior} from './ActionButtonBehavior';
|
||||
|
||||
export function actionDecorator(actionId) {
|
||||
return mapContext(mapActionBehavior(actionId));
|
||||
return Comp => props => <ActionButtonBehavior actionId={actionId} >
|
||||
{bProps => <Comp {...bProps} {...props} />}
|
||||
</ActionButtonBehavior>;
|
||||
}
|
||||
3
web/app/cad/dom/components/AppContext.js
Normal file
3
web/app/cad/dom/components/AppContext.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import React from "react";
|
||||
|
||||
export const AppContext = React.createContext({});
|
||||
|
|
@ -6,12 +6,13 @@ import ls from './HeadsUpToolbar.less';
|
|||
import {combine} from '../../../../../modules/lstream';
|
||||
|
||||
export const HeadsUpToolbar = connect(streams => combine(
|
||||
streams.ui.toolbars.headsUp,
|
||||
streams.ui.toolbars.headsUpQuickActions).map(([actions, quickActions]) => ({actions, quickActions})))(
|
||||
function HeadsUpToolbar({actions, quickActions}) {
|
||||
streams.ui.toolbars.headsUp,
|
||||
streams.ui.toolbars.headsUpShowTitles,
|
||||
streams.ui.toolbars.headsUpQuickActions).map(([actions, showTitles, quickActions]) => ({actions, showTitles, quickActions})))(
|
||||
function HeadsUpToolbar({actions, showTitles, quickActions}) {
|
||||
return <Toolbar flat>
|
||||
<div className={ls.mainActions}>
|
||||
<ToolbarActionButtons actions={actions} />
|
||||
<ToolbarActionButtons actions={actions} showTitles={showTitles}/>
|
||||
</div>
|
||||
|
||||
<div className={ls.quickButtons}>
|
||||
|
|
|
|||
|
|
@ -14,5 +14,9 @@
|
|||
.mainActions {
|
||||
flex: 1 1;
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
flex-wrap: wrap;
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,10 @@ import connect from 'ui/connect';
|
|||
import Fa from 'ui/components/Fa';
|
||||
import {toIdAndOverrides} from '../../actions/actionRef';
|
||||
import {isMenuAction} from '../menu/menuPlugin';
|
||||
import {combine, merger} from 'lstream';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import decoratorChain from '../../../../../modules/ui/decoratorChain';
|
||||
import {combine} from 'lstream';
|
||||
import {menuAboveElementHint} from '../menu/menuUtils';
|
||||
import {actionDecorator} from '../../actions/actionDecorators';
|
||||
import {useStream} from "../../../../../modules/ui/effects";
|
||||
import {ActionButtonBehavior} from "../../actions/ActionButtonBehavior";
|
||||
|
||||
export default function PlugableControlBar() {
|
||||
return <ControlBar left={<LeftGroup />} right={<RightGroup />}/>;
|
||||
|
|
@ -42,14 +41,17 @@ class ActionButton extends React.Component {
|
|||
const LeftGroup = connect(streams => streams.ui.controlBars.left.map(actions => ({actions})))(ButtonGroup);
|
||||
const RightGroup = connect(streams => streams.ui.controlBars.right.map(actions => ({actions})))(ButtonGroup);
|
||||
|
||||
const ConnectedActionButton = decoratorChain(
|
||||
function ConnectedActionButton(props) {
|
||||
|
||||
connect(
|
||||
(streams, props) => combine(
|
||||
streams.action.appearance[props.actionId],
|
||||
streams.action.state[props.actionId]).map(merger)),
|
||||
|
||||
actionDecorator(props => props.actionId)
|
||||
)
|
||||
(ActionButton);
|
||||
const actionId = props.actionId;
|
||||
const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId]));
|
||||
if (!stream) {
|
||||
return null;
|
||||
}
|
||||
const [actionAppearance, actionState] = stream;
|
||||
|
||||
return <ActionButtonBehavior actionId={actionId}>
|
||||
{behaviourProps => <ActionButton {...behaviourProps} {...actionAppearance} {...actionState} {...props} />}
|
||||
</ActionButtonBehavior>;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,12 @@
|
|||
import React from 'react';
|
||||
import connect from 'ui/connect';
|
||||
import Fa from 'ui/components/Fa';
|
||||
import Toolbar, {ToolbarButton} from 'ui/components/Toolbar';
|
||||
import Toolbar, {ToolbarBraker, ToolbarButton, ToolbarGroup, ToolbarSplitter} from 'ui/components/Toolbar';
|
||||
import ImgIcon from 'ui/components/ImgIcon';
|
||||
import {toIdAndOverrides} from '../../actions/actionRef';
|
||||
import {mapActionBehavior} from '../../actions/actionButtonBehavior';
|
||||
import {ActionButtonBehavior} from '../../actions/ActionButtonBehavior';
|
||||
import capitalize from 'gems/capitalize';
|
||||
import decoratorChain from 'ui/decoratorChain';
|
||||
import {combine, merger} from 'lstream';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import {ToolbarSplitter} from 'ui/components/Toolbar';
|
||||
import {combine} from 'lstream';
|
||||
import {useStream} from "../../../../../modules/ui/effects";
|
||||
|
||||
function ConfigurableToolbar({actions, size, ...props}) {
|
||||
return <Toolbar size={size} {...props}>
|
||||
|
|
@ -17,31 +14,43 @@ function ConfigurableToolbar({actions, size, ...props}) {
|
|||
</Toolbar>
|
||||
}
|
||||
|
||||
export function ToolbarActionButtons({actions, size}) {
|
||||
export function ToolbarActionButtons({actions, showTitles, size}) {
|
||||
return actions.map((actionRef, i) => {
|
||||
if (actionRef === '-') {
|
||||
return <ToolbarSplitter key={'ToolbarSplitter' + i}/>;
|
||||
return <ToolbarSplitter key={'ToolbarSplitter' + i} />;
|
||||
} else if (actionRef === '|') {
|
||||
return <ToolbarBraker key={'ToolbarBraker' + i} />;
|
||||
} else if (Array.isArray(actionRef)) {
|
||||
return <div key={'ToolbarGroup' + i}>
|
||||
<ToolbarGroup><ToolbarActionButtons actions={actionRef.slice(0, actionRef.length / 2)} showTitles={showTitles} size={size} /></ToolbarGroup>
|
||||
<ToolbarGroup><ToolbarActionButtons actions={actionRef.slice(actionRef.length / 2, actionRef.length)} showTitles={showTitles} size={size} /></ToolbarGroup>
|
||||
</div>;
|
||||
}
|
||||
let [id, overrides] = toIdAndOverrides(actionRef);
|
||||
return <ConnectedActionButton actionId={id} key={id} size={size} {...overrides} />
|
||||
return <ConnectedActionButton actionId={id} key={id} size={size} {...overrides} noLabel={!showTitles}/>
|
||||
});
|
||||
}
|
||||
|
||||
function ActionButton({label, icon96, icon32, cssIcons, symbol, size, noLabel, enabled, visible, actionId, ...props}) {
|
||||
function ActionButton({label, icon, icon96, icon32, cssIcons, symbol, size, noLabel, enabled, visible, actionId, ...props}) {
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let smallOrMedium = size === 'medium' || size === 'small';
|
||||
let icon;
|
||||
if (smallOrMedium) {
|
||||
if (cssIcons) {
|
||||
icon = <Fa fa={cssIcons} fw />;
|
||||
} else if (icon32) {
|
||||
icon = <ImgIcon url={icon32} size={size === 'small' ? 16 : 24} />;
|
||||
if (icon) {
|
||||
const Icon = icon;
|
||||
icon = <Icon />;
|
||||
}
|
||||
if (!icon) {
|
||||
if (smallOrMedium) {
|
||||
if (cssIcons) {
|
||||
icon = <Fa fa={cssIcons} fw />;
|
||||
} else if (icon32) {
|
||||
icon = <ImgIcon url={icon32} size={size === 'small' ? 16 : 24} />;
|
||||
}
|
||||
} else {
|
||||
icon = <ImgIcon url={icon96} size={48} />;
|
||||
}
|
||||
} else {
|
||||
icon = <ImgIcon url={icon96} size={48} />;
|
||||
}
|
||||
if (!icon) {
|
||||
icon = <span>{symbol||(label&&label.charAt(0))}</span>;
|
||||
|
|
@ -56,15 +65,27 @@ function ActionButton({label, icon96, icon32, cssIcons, symbol, size, noLabel, e
|
|||
</ToolbarButton>
|
||||
}
|
||||
|
||||
export const ConnectedActionButton = decoratorChain(
|
||||
connect((streams, {actionId}) => combine(streams.action.appearance[actionId], streams.action.state[actionId]).map(merger)),
|
||||
mapContext(mapActionBehavior(props => props.actionId))
|
||||
)
|
||||
(ActionButton);
|
||||
export function ConnectedActionButton(props) {
|
||||
|
||||
const actionId = props.actionId;
|
||||
const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId]));
|
||||
if (!stream) {
|
||||
return null;
|
||||
}
|
||||
const [actionAppearance, actionState] = stream;
|
||||
|
||||
return<ActionButtonBehavior actionId={actionId}>
|
||||
{behaviourProps => <ActionButton {...behaviourProps} {...actionAppearance} {...actionState} {...props} />}
|
||||
</ActionButtonBehavior>;
|
||||
|
||||
}
|
||||
|
||||
export function createPlugableToolbar(streamSelector) {
|
||||
return decoratorChain(
|
||||
connect(streams => streamSelector(streams).map(actions => ({actions})))
|
||||
)
|
||||
(props => <ConfigurableToolbar {...props} />);
|
||||
return function (props) {
|
||||
const actions = useStream(ctx => streamSelector(ctx.streams));
|
||||
if (!actions) {
|
||||
return null;
|
||||
}
|
||||
return <ConfigurableToolbar actions={actions} {...props} />;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import React from 'react';
|
||||
import connect from 'ui/connect';
|
||||
|
||||
@connect(streams => streams.sketcher.sketchingMode.map(sketchingMode => ({visible: sketchingMode})))
|
||||
export default class SketcherMode extends React.Component {
|
||||
|
||||
render() {
|
||||
if (!this.props.visible) {
|
||||
return null;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +1,19 @@
|
|||
import React from 'react';
|
||||
import PlugableControlBar from './PlugableControlBar';
|
||||
import ls from './View3d.less';
|
||||
import Abs from 'ui/components/Abs';
|
||||
import UISystem from './UISystem';
|
||||
import WizardManager from '../../craft/wizard/components/WizardManager';
|
||||
import FloatView from './FloatView';
|
||||
import HistoryTimeline from '../../craft/ui/HistoryTimeline';
|
||||
import SelectedModificationInfo from '../../craft/ui/SelectedModificationInfo';
|
||||
import BottomStack from './BottomStack';
|
||||
import SketcherToolbars from './SketcherToolbars';
|
||||
import CameraControl from './CameraControl';
|
||||
import HeadsUpHelper from './HeadsUpHelper';
|
||||
import {HeadsUpToolbar} from './HeadsUpToolbar';
|
||||
import {SketchObjectExplorer} from '../../../sketcher/components/SketchObjectExplorer';
|
||||
import SketcherMode from './SketcherMode';
|
||||
import SketcherMode from '../../sketch/components/SketcherMode';
|
||||
import {ConstraintExplorer} from '../../../sketcher/components/ConstraintExplorer';
|
||||
import {Scope} from "../../../sketcher/components/Scope";
|
||||
import {InplaceSketcher} from "../../sketch/components/InplaceSketcher";
|
||||
|
||||
|
||||
export default class View3d extends React.Component {
|
||||
|
|
@ -37,20 +36,16 @@ export default class View3d extends React.Component {
|
|||
|
||||
<div className={ls.middleSection}>
|
||||
<SketcherMode>
|
||||
<div className={ls.overlayingPanel} >
|
||||
<SketchObjectExplorer />
|
||||
<ConstraintExplorer />
|
||||
</div>
|
||||
<InplaceSketcher>
|
||||
<div className={ls.overlayingPanel} >
|
||||
<Scope><SketchObjectExplorer /></Scope>
|
||||
<Scope><ConstraintExplorer /></Scope>
|
||||
</div>
|
||||
</InplaceSketcher>
|
||||
</SketcherMode>
|
||||
<div className={ls.wizardArea} >
|
||||
<WizardManager/>
|
||||
</div>
|
||||
<SketcherMode>
|
||||
<div className={ls.spring} />
|
||||
<div className={ls.middleRight}>
|
||||
<SketcherToolbars />
|
||||
</div>
|
||||
</SketcherMode>
|
||||
</div>
|
||||
|
||||
<div className={ls.bottomStack}>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
|
|||
|
||||
import 'ui/styles/init/index.less';
|
||||
import AppTabs from "./AppTabs";
|
||||
import {StreamsContext} from "../../../../../modules/ui/streamsContext";
|
||||
import {AppContext} from "./AppContext";
|
||||
|
||||
|
||||
export default class WebApplication extends React.Component {
|
||||
|
|
@ -14,7 +16,12 @@ export default class WebApplication extends React.Component {
|
|||
|
||||
|
||||
render() {
|
||||
return <AppTabs />
|
||||
const {appContext} = this.props;
|
||||
return <StreamsContext.Provider value={appContext}>
|
||||
<AppContext.Provider value={appContext}>
|
||||
<AppTabs />
|
||||
</AppContext.Provider>
|
||||
</StreamsContext.Provider>
|
||||
}
|
||||
|
||||
getChildContext() {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ import React from 'react';
|
|||
import Menu, {MenuItem, MenuSeparator} from 'ui/components/Menu';
|
||||
import Filler from 'ui/components/Filler';
|
||||
import Fa from 'ui/components/Fa';
|
||||
import {mapActionBehavior} from '../../actions/actionButtonBehavior';
|
||||
import {ActionButtonBehavior} from '../../actions/ActionButtonBehavior';
|
||||
import connect from 'ui/connect';
|
||||
import {combine, merger} from 'lstream';
|
||||
import mapContext from 'ui/mapContext';
|
||||
import decoratorChain from 'ui/decoratorChain';
|
||||
import {useStream} from "../../../../../modules/ui/effects";
|
||||
|
||||
function MenuHolder({menus}) {
|
||||
return menus.map(({id, actions}) => <ConnectedActionMenu key={id} menuId={id} actions={actions} />);
|
||||
|
|
@ -55,17 +54,21 @@ const ConnectedActionMenu = connect((streams, props) =>
|
|||
.map(([s, keymap]) => ({...s, keymap})))
|
||||
(ActionMenu);
|
||||
|
||||
export function ConnectedMenuItem(props) {
|
||||
|
||||
let ConnectedMenuItem = decoratorChain(
|
||||
const actionId = props.actionId;
|
||||
const stream = useStream(ctx => combine(ctx.streams.action.appearance[actionId], ctx.streams.action.state[actionId]));
|
||||
if (!stream) {
|
||||
return null;
|
||||
}
|
||||
const [actionAppearance, actionState] = stream;
|
||||
|
||||
return <ActionButtonBehavior actionId={actionId}>
|
||||
{behaviourProps => <ActionMenuItem {...behaviourProps} {...actionAppearance} {...actionState} {...props} />}
|
||||
</ActionButtonBehavior>;
|
||||
|
||||
}
|
||||
|
||||
connect((streams, {actionId}) =>
|
||||
combine(
|
||||
streams.action.state[actionId],
|
||||
streams.action.appearance[actionId]).map(merger)),
|
||||
|
||||
mapContext(mapActionBehavior(props => props.actionId))
|
||||
)
|
||||
(ActionMenuItem);
|
||||
|
||||
export default connect(streams => streams.ui.menu.all.map(menus => ({menus})))(MenuHolder);
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export function defineStreams({streams}) {
|
|||
},
|
||||
toolbars: {
|
||||
headsUp: state([]),
|
||||
headsUpShowTitles: state(true),
|
||||
headsUpQuickActions: state([]),
|
||||
sketcherGeneral: state([]),
|
||||
sketcherConstraints: state([]),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import React from 'react';
|
|||
import OperationHistory from '../craft/ui/OperationHistory';
|
||||
import Expressions from '../expressions/Expressions';
|
||||
|
||||
export const STANDARD_MODE_HEADS_UP_TOOLBAR = ['DATUM_CREATE', 'PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE', 'LOFT',
|
||||
'-', 'FILLET', '-', 'INTERSECTION', 'SUBTRACT', 'UNION'];
|
||||
|
||||
export function activate({services, streams}) {
|
||||
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'Donate', 'GitHub'];
|
||||
streams.ui.controlBars.right.value = [
|
||||
|
|
@ -16,8 +19,7 @@ export function activate({services, streams}) {
|
|||
['ShowSketches', {label: 'sketches'}], ['DeselectAll', {label: null}], ['ToggleCameraMode', {label: null}]
|
||||
];
|
||||
|
||||
streams.ui.toolbars.headsUp.value = ['DATUM_CREATE', 'PLANE', 'EditFace', 'EXTRUDE', 'CUT', 'REVOLVE', 'LOFT',
|
||||
'-', 'FILLET', '-', 'INTERSECTION', 'SUBTRACT', 'UNION'];
|
||||
streams.ui.toolbars.headsUp.value = STANDARD_MODE_HEADS_UP_TOOLBAR;
|
||||
streams.ui.toolbars.headsUpQuickActions.value = ['Save', 'StlExport'];
|
||||
|
||||
services.action.registerActions(CoreActions);
|
||||
|
|
|
|||
20
web/app/cad/sketch/components/InplaceSketcher.jsx
Normal file
20
web/app/cad/sketch/components/InplaceSketcher.jsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
import {useStream} from "../../../../../modules/ui/effects";
|
||||
import {StreamsContext} from "../../../../../modules/ui/streamsContext";
|
||||
import {SketcherAppContext} from "../../../sketcher/components/SketcherAppContext";
|
||||
import {Scope} from "../../../sketcher/components/Scope";
|
||||
|
||||
export function InplaceSketcher({children}) {
|
||||
|
||||
const sketcherAppContext = useStream(ctx => ctx.streams.sketcher.sketcherAppContext);
|
||||
|
||||
if (sketcherAppContext === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <SketcherAppContext.Provider value={sketcherAppContext}>
|
||||
<StreamsContext.Provider value={sketcherAppContext}>
|
||||
<Scope>{children}</Scope>
|
||||
</StreamsContext.Provider>
|
||||
</SketcherAppContext.Provider>
|
||||
}
|
||||
13
web/app/cad/sketch/components/SketcherMode.jsx
Normal file
13
web/app/cad/sketch/components/SketcherMode.jsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import {useStream} from "../../../../../modules/ui/effects";
|
||||
|
||||
export default function SketcherMode({children}) {
|
||||
|
||||
const visible = useStream(ctx => ctx.streams.sketcher.sketchingMode);
|
||||
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import ls from './SketcherToolbars.less';
|
||||
import {createPlugableToolbar} from './PlugableToolbar';
|
||||
import {createPlugableToolbar} from '../../dom/components/PlugableToolbar';
|
||||
import '../../../sketcher/actions';
|
||||
|
||||
export default function SketcherToolbars({visible}) {
|
||||
return <div className={ls.sketcherToolbars}>
|
||||
|
|
@ -1,18 +1,22 @@
|
|||
import {Viewer} from '../../sketcher/viewer2d';
|
||||
import {IO} from '../../sketcher/io';
|
||||
import {DelegatingPanTool} from '../../sketcher/tools/pan';
|
||||
import {Matrix4} from 'three/src/math/Matrix4';
|
||||
import {ORIGIN} from '../../math/l3space';
|
||||
import {CAMERA_MODE} from '../scene/viewer';
|
||||
import DPR from 'dpr';
|
||||
import sketcherStreams from '../../sketcher/sketcherStreams';
|
||||
import {SKETCHER_MODE_HEADS_UP_ACTIONS} from "./sketcherUIContrib";
|
||||
import {createEssentialAppContext} from "../../sketcher/sketcherContext";
|
||||
import {STANDARD_MODE_HEADS_UP_TOOLBAR} from "../part/uiConfigPlugin";
|
||||
|
||||
export class InPlaceSketcher {
|
||||
|
||||
constructor(ctx) {
|
||||
this.face = null; // should be only one in the state
|
||||
this.ctx = ctx;
|
||||
this.viewer = null;
|
||||
this.sketcherAppContext = null;
|
||||
}
|
||||
|
||||
get viewer() {
|
||||
return this.sketcherAppContext ? this.sketcherAppContext.viewer : null;
|
||||
}
|
||||
|
||||
get inEditMode() {
|
||||
|
|
@ -33,18 +37,21 @@ export class InPlaceSketcher {
|
|||
canvas.style.bottom = 0;
|
||||
|
||||
container.appendChild(canvas);
|
||||
this.viewer = new Viewer(canvas, IO);
|
||||
this.sketcherAppContext = createEssentialAppContext(canvas);
|
||||
this.viewer.parametricManager.externalConstantResolver = this.ctx.services.expressions.evaluateExpression;
|
||||
this.ctx.streams.sketcherApp = this.viewer.streams;
|
||||
|
||||
this.syncWithCamera();
|
||||
this.viewer.toolManager.setDefaultTool(new DelegatingPanTool(this.viewer, viewer3d.sceneSetup.renderer.domElement));
|
||||
viewer3d.sceneSetup.trackballControls.addEventListener( 'change', this.onCameraChange);
|
||||
|
||||
this.ctx.streams.ui.toolbars.headsUp.next(SKETCHER_MODE_HEADS_UP_ACTIONS);
|
||||
this.ctx.streams.ui.toolbars.headsUpShowTitles.next(false);
|
||||
|
||||
let sketchData = this.ctx.services.storage.get(this.sketchStorageKey);
|
||||
this.viewer.historyManager.init(sketchData);
|
||||
this.viewer.io.loadSketch(sketchData);
|
||||
this.ctx.streams.sketcher.sketchingFace.value = face;
|
||||
this.ctx.streams.sketcher.sketchingFace.next(face);
|
||||
this.ctx.streams.sketcher.sketcherAppContext.next(this.sketcherAppContext);
|
||||
}
|
||||
|
||||
get sketchStorageKey() {
|
||||
|
|
@ -60,9 +67,11 @@ export class InPlaceSketcher {
|
|||
this.face = null;
|
||||
this.viewer.canvas.parentNode.removeChild(this.viewer.canvas);
|
||||
this.viewer.dispose();
|
||||
this.viewer = null;
|
||||
this.ctx.streams.sketcher.sketchingFace.value = null;
|
||||
this.ctx.streams.sketcherApp = null;
|
||||
this.sketcherAppContext = null;
|
||||
this.ctx.streams.sketcher.sketchingFace.next(null);
|
||||
this.ctx.streams.sketcher.sketcherAppContext.next(null);
|
||||
this.ctx.streams.ui.toolbars.headsUp.next(STANDARD_MODE_HEADS_UP_TOOLBAR);
|
||||
this.ctx.streams.ui.toolbars.headsUpShowTitles.next(true);
|
||||
viewer3d.requestRender();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import {FcCancel, FcCheckmark} from "react-icons/fc";
|
||||
import {RiExternalLinkLine} from "react-icons/ri";
|
||||
|
||||
export default [
|
||||
{
|
||||
id: 'sketchSaveAndExit',
|
||||
appearance: {
|
||||
info: 'save sketch changes and exit',
|
||||
label: 'commit',
|
||||
cssIcons: ['check'],
|
||||
icon: FcCheckmark,
|
||||
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
services.sketcher.inPlaceEditor.save();
|
||||
|
|
@ -16,7 +20,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'drop sketch changes and exit',
|
||||
label: 'exit sketch',
|
||||
cssIcons: ['times'],
|
||||
icon: FcCancel,
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
services.sketcher.inPlaceEditor.exit();
|
||||
|
|
@ -27,7 +31,7 @@ export default [
|
|||
appearance: {
|
||||
info: 'save changes and open sketch 2D in a tab',
|
||||
label: '2D',
|
||||
cssIcons: ['external-link'],
|
||||
icon: RiExternalLinkLine,
|
||||
},
|
||||
invoke: ({services}) => {
|
||||
let face = services.sketcher.inPlaceEditor.face;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ import {DelegatingPanTool} from "../../sketcher/tools/pan";
|
|||
export function defineStreams(ctx) {
|
||||
ctx.streams.sketcher = {
|
||||
update: stream(),
|
||||
sketchingFace: state(null)
|
||||
sketchingFace: state(null),
|
||||
sketcherAppContext: state(null)
|
||||
};
|
||||
ctx.streams.sketcher.sketchingMode = ctx.streams.sketcher.sketchingFace.map(face => !!face);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +1,61 @@
|
|||
import SketcherToolActions from './sketcherToolActions';
|
||||
import SketcherConstrainsActions from './sketcherConstraintsActions';
|
||||
import SketcherControlActions from './sketcherControlActions';
|
||||
import {state} from '../../../../modules/lstream';
|
||||
import {startOperation} from "../../sketcher/actions";
|
||||
import objectToolActions from '../../sketcher/actions/objectToolActions';
|
||||
import measureActions from '../../sketcher/actions/measureActions';
|
||||
import {insertAfter} from '../../../../modules/gems/iterables';
|
||||
import operationActions from "../../sketcher/actions/operationActions";
|
||||
import constraintGlobalActions from "../../sketcher/actions/constraintGlobalActions";
|
||||
import generalToolActions from "../../sketcher/actions/generalToolActions";
|
||||
import sketcherControlActions from "./sketcherControlActions";
|
||||
|
||||
export default function ({services, streams}) {
|
||||
services.action.registerActions(sketcherControlActions);
|
||||
services.action.registerActions([
|
||||
...constraintGlobalActions,
|
||||
...measureActions,
|
||||
...generalToolActions,
|
||||
...objectToolActions,
|
||||
...operationActions,
|
||||
|
||||
].map(convertSketcherAction));
|
||||
|
||||
}
|
||||
|
||||
const SKETCHER_PREFIX = 'sketcher.';
|
||||
|
||||
function toSketcherActionId(id) {
|
||||
return SKETCHER_PREFIX + id;
|
||||
}
|
||||
|
||||
function convertSketcherAction(action) {
|
||||
|
||||
return {
|
||||
id: toSketcherActionId(action.id),
|
||||
appearance: {
|
||||
icon: action.icon,
|
||||
label: action.shortName,
|
||||
info: action.description,
|
||||
},
|
||||
invoke: ({services}, e) => action.invoke(services.sketcher.inPlaceEditor.sketcherAppContext)
|
||||
}
|
||||
}
|
||||
export const SKETCHER_MODE_HEADS_UP_ACTIONS = [
|
||||
['sketchSaveAndExit', 'sketchExit'],
|
||||
'-',
|
||||
generalToolActions.map(a => toSketcherActionId(a.id)),
|
||||
'-',
|
||||
[
|
||||
...objectToolActions.map(a => toSketcherActionId(a.id)),
|
||||
toSketcherActionId('Offset'),
|
||||
],
|
||||
'-',
|
||||
measureActions.map(a => toSketcherActionId(a.id)),
|
||||
'-',
|
||||
constraintGlobalActions.map(a => toSketcherActionId(a.id)),
|
||||
'-',
|
||||
['sketchOpenInTab']
|
||||
];
|
||||
|
||||
insertAfter(SKETCHER_MODE_HEADS_UP_ACTIONS, SKETCHER_PREFIX + 'Export', '-');
|
||||
insertAfter(SKETCHER_MODE_HEADS_UP_ACTIONS, SKETCHER_PREFIX + 'PanTool', '-');
|
||||
insertAfter(SKETCHER_MODE_HEADS_UP_ACTIONS, SKETCHER_PREFIX + 'BezierTool', '-');
|
||||
|
||||
services.action.registerActions(SketcherToolActions);
|
||||
services.action.registerActions(SketcherConstrainsActions);
|
||||
services.action.registerActions(SketcherControlActions);
|
||||
|
||||
streams.ui.toolbars.sketcherGeneral.value = [
|
||||
'sketchReferencePoint',
|
||||
'sketchPanTool',
|
||||
'sketchAddPoint',
|
||||
'sketchAddSegment',
|
||||
'sketchAddMultiSegment',
|
||||
'sketchAddArc',
|
||||
'sketchAddCircle',
|
||||
'sketchAddEllipse',
|
||||
'sketchAddEllipticalArc',
|
||||
'sketchAddCubicBezierSpline',
|
||||
'sketchAddRectangle',
|
||||
'sketchOffsetTool',
|
||||
'sketchAddFillet',
|
||||
'sketchAddDim',
|
||||
'sketchAddHDim',
|
||||
'sketchAddVDim',
|
||||
'sketchCircleDim',
|
||||
];
|
||||
streams.ui.toolbars.sketcherConstraints.value = [
|
||||
'sketchConstraint_coincident',
|
||||
'sketchConstraint_verticalConstraint',
|
||||
'sketchConstraint_horizontalConstraint',
|
||||
'sketchConstraint_parallelConstraint',
|
||||
'sketchConstraint_perpendicularConstraint',
|
||||
'sketchConstraint_P2LDistanceConstraint',
|
||||
'sketchConstraint_P2PDistanceConstraint',
|
||||
'sketchConstraint_RadiusConstraint',
|
||||
'sketchConstraint_EntityEqualityConstraint',
|
||||
'sketchConstraint_tangentConstraint',
|
||||
'sketchConstraint_lockConstraint',
|
||||
'sketchConstraint_pointOnLine',
|
||||
'sketchConstraint_pointOnArc',
|
||||
'sketchConstraint_pointInMiddle',
|
||||
'sketchConstraint_llAngle',
|
||||
'sketchConstraint_symmetry',
|
||||
'sketchConstraint_mirror',
|
||||
'sketchConstraint_lockConvex'
|
||||
];
|
||||
streams.ui.toolbars.sketcherControl.value = [
|
||||
'sketchSaveAndExit', 'sketchOpenInTab', 'sketchExit'
|
||||
];
|
||||
}
|
||||
31
web/app/sketcher/actions/generalToolActions.js
Normal file
31
web/app/sketcher/actions/generalToolActions.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import {ReferencePointTool} from "../tools/origin";
|
||||
import {IoIosHand} from "react-icons/io";
|
||||
import {GiCrosshair} from "react-icons/gi";
|
||||
|
||||
export default [
|
||||
{
|
||||
id: 'PanTool',
|
||||
shortName: 'Pan',
|
||||
kind: 'Tool',
|
||||
description: 'Pan mode',
|
||||
icon: IoIosHand,
|
||||
|
||||
invoke: (ctx) => {
|
||||
ctx.viewer.toolManager.releaseControl();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'ReferencePointTool',
|
||||
shortName: 'Set Origin',
|
||||
kind: 'Tool',
|
||||
description: 'Sets reference point for commands',
|
||||
icon: GiCrosshair,
|
||||
command: 'origin',
|
||||
invoke: (ctx) => {
|
||||
ctx.viewer.toolManager.takeControl(new ReferencePointTool(ctx.viewer));
|
||||
}
|
||||
|
||||
},
|
||||
]
|
||||
|
|
@ -4,9 +4,10 @@ import {toast} from "react-toastify";
|
|||
import operationActions from "./operationActions";
|
||||
import constraintGlobalActions from "./constraintGlobalActions";
|
||||
import measureActions from "./measureActions";
|
||||
import toolActions from "./toolActions";
|
||||
import objectToolActions from "./objectToolActions";
|
||||
import commonActions from "./commonActions";
|
||||
import exportActions from "./exportActions";
|
||||
import generalToolActions from "./generalToolActions";
|
||||
|
||||
const ALL_CONTEXTUAL_ACTIONS = [
|
||||
...constraintActions,
|
||||
|
|
@ -16,7 +17,8 @@ const ALL_CONTEXTUAL_ACTIONS = [
|
|||
const ACTIONS = [
|
||||
...constraintGlobalActions,
|
||||
...measureActions,
|
||||
...toolActions,
|
||||
...generalToolActions,
|
||||
...objectToolActions,
|
||||
...commonActions,
|
||||
...exportActions
|
||||
//keep going here
|
||||
|
|
|
|||
|
|
@ -16,39 +16,9 @@ import {EllipseTool} from "../tools/ellipse";
|
|||
import {AddPointTool} from "../tools/point";
|
||||
import {AddArcTool} from "../tools/arc";
|
||||
import {EditCircleTool} from "../tools/circle";
|
||||
import {IoIosHand} from "react-icons/io";
|
||||
import {ReferencePointTool} from "../tools/origin";
|
||||
import {GiCrosshair} from "react-icons/gi";
|
||||
|
||||
export default [
|
||||
|
||||
{
|
||||
id: 'PanTool',
|
||||
shortName: 'Pan',
|
||||
kind: 'Tool',
|
||||
description: 'Pan mode',
|
||||
icon: IoIosHand,
|
||||
|
||||
invoke: (ctx) => {
|
||||
ctx.viewer.toolManager.releaseControl();
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'ReferencePointTool',
|
||||
shortName: 'Set Origin',
|
||||
kind: 'Tool',
|
||||
description: 'Sets reference point for commands',
|
||||
icon: GiCrosshair,
|
||||
command: 'origin',
|
||||
invoke: (ctx) => {
|
||||
ctx.viewer.toolManager.takeControl(new ReferencePointTool(ctx.viewer));
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'PointTool',
|
||||
shortName: 'Point',
|
||||
|
|
@ -8,8 +8,8 @@ import CheckboxControl from "ui/components/controls/CheckboxControl";
|
|||
import Window from "ui/components/Window";
|
||||
import Field from "ui/components/controls/Field";
|
||||
import Label from "../../../../modules/ui/components/controls/Label";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {EMPTY_OBJECT} from "../../../../modules/gems/objects";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function ConstraintEditor() {
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@ import React, {useContext, useEffect} from 'react';
|
|||
import ls from './ConstraintExplorer.less';
|
||||
import Fa from 'ui/components/Fa';
|
||||
import {useStream} from "ui/effects";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import cx from 'classnames';
|
||||
import {editConstraint} from "./ConstraintEditor";
|
||||
import {NoIcon} from "../icons/NoIcon";
|
||||
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function ConstraintExplorer(props) {
|
||||
return <React.Fragment>
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import React, {useContext} from 'react';
|
|||
import ls from './ContextualControls.less';
|
||||
import {matchAvailableActions} from "../actions";
|
||||
import {useStream} from "../../../../modules/ui/effects";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {MatchIndex, matchSelection} from "../selectionMatcher";
|
||||
import {ConstraintButton, GeneratorButton} from "./ConstraintExplorer";
|
||||
import {Columnizer} from "../../../../modules/ui/components/Columnizer";
|
||||
import {NoIcon} from "../icons/NoIcon";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function ContextualControls() {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import React, {useContext, useMemo, useState} from 'react';
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {useStreamWithUpdater} from "ui/effects";
|
||||
import Window, {DIRECTIONS} from "ui/components/Window";
|
||||
import App2D from "../sketcherContext";
|
||||
import Stack from "ui/components/Stack";
|
||||
import Button from "ui/components/controls/Button";
|
||||
import {RiDeleteBinLine} from "react-icons/ri";
|
||||
import {SKETCHER_STORAGE_PREFIX} from "../project";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function SketchManager() {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,84 +1,85 @@
|
|||
import React from 'react';
|
||||
import React, {useContext, useState} from 'react';
|
||||
import cx from 'classnames';
|
||||
import ls from './SketchObjectExplorer.less'
|
||||
|
||||
import connect from 'ui/connect';
|
||||
import {combine} from 'lstream';
|
||||
import mapContext from '../../../../modules/ui/mapContext';
|
||||
import {useStream} from "../../../../modules/ui/effects";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
@connect(streams => combine(streams.sketcherApp.objects, streams.sketcherApp.selection)
|
||||
.map(([objects, selection]) => ({
|
||||
objects,
|
||||
selection
|
||||
})))
|
||||
@mapContext(ctx => ({
|
||||
select: (obj, exclusive) => {
|
||||
let viewer = ctx.services.sketcher.inPlaceEditor.viewer;
|
||||
|
||||
export function SketchObjectExplorer() {
|
||||
|
||||
const [modification, setModification] = useState(0);
|
||||
const stream = useStream(ctx => combine(ctx.viewer.streams.objects, ctx.viewer.streams.selection));
|
||||
const ctx = useContext(SketcherAppContext);
|
||||
if (!stream) {
|
||||
return null
|
||||
}
|
||||
const [objects, selection] = stream;
|
||||
|
||||
const select = (obj, exclusive) => {
|
||||
let viewer = ctx.viewer;
|
||||
viewer.select([obj], exclusive);
|
||||
viewer.refresh();
|
||||
},
|
||||
deselect: obj => {
|
||||
let viewer = ctx.services.sketcher.inPlaceEditor.viewer;
|
||||
};
|
||||
const deselect = obj => {
|
||||
let viewer = ctx.viewer;
|
||||
viewer.deselect(obj);
|
||||
viewer.refresh();
|
||||
},
|
||||
setRole: (obj, role) => {
|
||||
let viewer = ctx.services.sketcher.inPlaceEditor.viewer;
|
||||
};
|
||||
const setRole = (obj, role) => {
|
||||
let viewer = ctx.viewer;
|
||||
if (obj.aux) {
|
||||
return;
|
||||
}
|
||||
obj.role = role;
|
||||
viewer.refresh();
|
||||
}
|
||||
}))
|
||||
export class SketchObjectExplorer extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {objects} = this.props;
|
||||
return <React.Fragment>
|
||||
<div className={ls.titleBar}>Objects</div>
|
||||
<div className={ls.scrollableArea}>
|
||||
{objects.map(o => <div key={o.id} className={cx(ls.objectItem, getClassName(o))}>
|
||||
<span className={ls.objectIcon}><img width="15px" src='img/vec/pointOnArc.svg' /></span>
|
||||
{this.getObjectRole(o)}
|
||||
<span onClick={e => this.tweakSelection(o, e.shiftKey)} className={cx(ls.objectTag, o.marked&&ls.selected)}>{o.simpleClassName} <span>{o.id}</span> </span>
|
||||
<span className={ls.menuButton}>...</span>
|
||||
</div>)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
tweakSelection(obj, shiftKey) {
|
||||
const tweakSelection = (obj, shiftKey) => {
|
||||
if (obj.marked) {
|
||||
this.props.deselect(obj);
|
||||
deselect(obj);
|
||||
} else {
|
||||
this.props.select(obj, !shiftKey);
|
||||
select(obj, !shiftKey);
|
||||
}
|
||||
}
|
||||
|
||||
tweakRole(obj) {
|
||||
if (obj.role === 'construction') {
|
||||
this.props.setRole(obj, null);
|
||||
} else if (obj.role === null) {
|
||||
this.props.setRole(obj, 'construction');
|
||||
}
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
getObjectRole(o) {
|
||||
const tweakRole = (obj) => {
|
||||
if (obj.role === 'construction') {
|
||||
setRole(obj, null);
|
||||
} else if (obj.role === null) {
|
||||
setRole(obj, 'construction');
|
||||
}
|
||||
setModification(count => count + 1);
|
||||
};
|
||||
|
||||
const getObjectRole = (o) => {
|
||||
if (o.aux) {
|
||||
return <span title="object is a readonly 3D feature/boundary" className={cx(ls.objectRole, ls.aux)}>B</span>
|
||||
} else if (o.role === 'construction') {
|
||||
return <span onClick={e => this.tweakRole(o)} title="construction object not used for 3D operations" className={cx(ls.objectRole, ls.construction)}>C</span>
|
||||
return <span onClick={e => tweakRole(o)} title="construction object not used for 3D operations"
|
||||
className={cx(ls.objectRole, ls.construction)}>C</span>
|
||||
} else {
|
||||
return <span onClick={e => this.tweakRole(o)} title="sketch object participates in 3D operations" className={cx(ls.objectRole, ls.sketch)}>S</span>
|
||||
return <span onClick={e => tweakRole(o)} title="sketch object participates in 3D operations"
|
||||
className={cx(ls.objectRole, ls.sketch)}>S</span>
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return <React.Fragment>
|
||||
<div className={ls.titleBar}>Objects</div>
|
||||
<div className={ls.scrollableArea}>
|
||||
{objects.map(o => <div key={o.id} className={cx(ls.objectItem, getClassName(o))}>
|
||||
<span className={ls.objectIcon}><img width="15px" src='img/vec/pointOnArc.svg'/></span>
|
||||
{getObjectRole(o)}
|
||||
<span onClick={e => tweakSelection(o, e.shiftKey)}
|
||||
className={cx(ls.objectTag, o.marked && ls.selected)}>{o.simpleClassName} <span>{o.id}</span> </span>
|
||||
<span className={ls.menuButton}>...</span>
|
||||
</div>)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
|
||||
function ObjectIcon({object}) {
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import {getSketcherAction} from "../actions";
|
||||
import React, {useContext} from "react";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function SketcherActionButton({actionId, text=false}) {
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ import {SketcherPropertiesView} from "./SketcherPropertiesView";
|
|||
import {SketcherDimensionView} from "./SketcherDimensionsView";
|
||||
import {SketcherTerminal} from "./TerminalView";
|
||||
|
||||
export const SketcherAppContext = React.createContext({});
|
||||
import {SketcherAppContext} from './SketcherAppContext';
|
||||
|
||||
export {SketcherAppContext};
|
||||
|
||||
export function SketcherApp({applicationContext}) {
|
||||
return <SketcherAppContext.Provider value={applicationContext}>
|
||||
|
|
|
|||
3
web/app/sketcher/components/SketcherAppContext.js
Normal file
3
web/app/sketcher/components/SketcherAppContext.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import React from "react";
|
||||
|
||||
export const SketcherAppContext = React.createContext({});
|
||||
|
|
@ -5,8 +5,8 @@ import Window from "ui/components/Window";
|
|||
import Stack from "ui/components/Stack";
|
||||
import ButtonGroup from "ui/components/controls/ButtonGroup";
|
||||
import Button from "ui/components/controls/Button";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {useStreamWithUpdater} from "../../../../modules/ui/effects";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
|
||||
export default function SketcherOperationWizard({}) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, {useContext} from 'react';
|
||||
import {useStream, useStreamWithUpdater} from "../../../../modules/ui/effects";
|
||||
import {useStreamWithUpdater} from "../../../../modules/ui/effects";
|
||||
import ls from "./ContextualControls.less";
|
||||
import {SketcherAppContext} from "./SketcherApp";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function StageControl() {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
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";
|
||||
import {SketcherAppContext} from "./SketcherAppContext";
|
||||
|
||||
export function TerminalView({visible, output, addToOutput, onClose, variantsSupplier, commandProcessor}) {
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,16 @@ import {Project} from "./project";
|
|||
|
||||
|
||||
export function createAppContext() {
|
||||
const ctx = createEssentialAppContext(document.getElementById('viewer'));
|
||||
ctx.project = new Project(ctx.viewer);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const viewer = new Viewer(document.getElementById('viewer'), IO);
|
||||
export function createEssentialAppContext(canvas) {
|
||||
|
||||
return {
|
||||
|
||||
viewer,
|
||||
project: new Project(viewer),
|
||||
viewer: new Viewer(canvas, IO),
|
||||
|
||||
get actions() {
|
||||
return getSketcherActionIndex();
|
||||
|
|
@ -35,4 +38,3 @@ export function createAppContext() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
import constraintGlobalActions from "./actions/constraintGlobalActions";
|
||||
import measureActions from "./actions/measureActions";
|
||||
import toolActions from "./actions/toolActions";
|
||||
import objectToolActions from "./actions/objectToolActions";
|
||||
import commonActions from "./actions/commonActions";
|
||||
import {removeInPlace} from "../../../modules/gems/iterables";
|
||||
import {insertAfter, removeInPlace} from "../../../modules/gems/iterables";
|
||||
import generalToolActions from "./actions/generalToolActions";
|
||||
|
||||
export const sketcherRightToolbarConfig = constraintGlobalActions.map(a => a.id);
|
||||
|
||||
export const sketcherTopToolbarConfig = [
|
||||
...commonActions.map(a => a.id),
|
||||
...toolActions.map(a => a.id),
|
||||
...generalToolActions.map(a => a.id),
|
||||
...objectToolActions.map(a => a.id),
|
||||
'Offset',
|
||||
'-',
|
||||
...measureActions.map(a => a.id)
|
||||
|
|
@ -18,11 +20,4 @@ insertAfter(sketcherTopToolbarConfig, 'Export', '-');
|
|||
insertAfter(sketcherTopToolbarConfig, 'PanTool', '-');
|
||||
insertAfter(sketcherTopToolbarConfig, 'BezierTool', '-');
|
||||
|
||||
function insertAfter(arr, item, toAdd) {
|
||||
const index = arr.indexOf(item);
|
||||
if (index !== -1) {
|
||||
arr.splice(index+1, 0, toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
removeInPlace(sketcherTopToolbarConfig, 'ToggleTerminal');
|
||||
Loading…
Reference in a new issue