mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-16 05:23:19 +01:00
Feature Request: Model history buttons for each item. #21
This commit is contained in:
parent
061465385d
commit
ac7f52325f
14 changed files with 218 additions and 82 deletions
3
web/app/3d/actions/all-actions.js
Normal file
3
web/app/3d/actions/all-actions.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export * from './core-actions'
|
||||
export * from './operation-actions'
|
||||
export * from './history-actions'
|
||||
|
|
@ -2,12 +2,12 @@ import * as ActionHelpers from './action-helpers'
|
|||
|
||||
export const EditFace = {
|
||||
cssIcons: ['file-picture-o'],
|
||||
label: 'edit sketch',
|
||||
label: 'sketch',
|
||||
icon96: 'img/3d/face-edit96.png',
|
||||
info: 'open sketcher for a face/plane',
|
||||
listens: ['selection'],
|
||||
update: ActionHelpers.checkForSelectedFaces(1),
|
||||
invoke: (app) => app.sketchFace()
|
||||
invoke: (app) => app.sketchSelectedFace()
|
||||
};
|
||||
|
||||
export const Save = {
|
||||
|
|
@ -74,7 +74,6 @@ export const LookAtSolid = {
|
|||
invoke: (app, e) => app.lookAtSolid(app.inputManager.context.attr('data-id'))
|
||||
};
|
||||
|
||||
|
||||
export const noIcon = {
|
||||
label: 'no icon'
|
||||
};
|
||||
57
web/app/3d/actions/history-actions.js
Normal file
57
web/app/3d/actions/history-actions.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
export const SetHistoryPointer = {
|
||||
label: 'set history',
|
||||
info: 'set history pointer to this modification item',
|
||||
invoke: (app) => {
|
||||
const mIndex = parseInt(modificationIndex(app));
|
||||
app.craft.historyPointer = mIndex;
|
||||
}
|
||||
};
|
||||
|
||||
export const OpenHistoryWizard = {
|
||||
label: 'edit operation',
|
||||
info: 'open wizard to change parameters of this operation',
|
||||
invoke: (app) => {
|
||||
const mIndex = parseInt(modificationIndex(app));
|
||||
if (mIndex != app.craft.historyPointer) {
|
||||
app.craft.historyPointer = mIndex;
|
||||
} else {
|
||||
const modification = app.craft.history[mIndex];
|
||||
app.ui.createWizardForOperation(modification);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const EditOperationSketch = {
|
||||
cssIcons: ['image'],
|
||||
label: 'sketch',
|
||||
info: 'edit the sketch assigned to this operation',
|
||||
invoke: (app) => {
|
||||
|
||||
const mIndex = parseInt(modificationIndex(app));
|
||||
const modification = app.craft.history[mIndex];
|
||||
if (!modification.face) {
|
||||
return;
|
||||
}
|
||||
if (mIndex != app.craft.historyPointer) {
|
||||
app.craft.historyPointer = mIndex;
|
||||
}
|
||||
const face = app.findFace(modification.face);
|
||||
app.sketchFace(face);
|
||||
}
|
||||
};
|
||||
|
||||
export const RemoveModification = {
|
||||
label: 'remove modification',
|
||||
info: 'remove this modification',
|
||||
invoke: (app) => {
|
||||
if (!confirm("This modification and all following modifications will be removed. Continue?")) {
|
||||
return;
|
||||
}
|
||||
const mIndex = parseInt(modificationIndex(app));
|
||||
app.craft.remove(mIndex);
|
||||
}
|
||||
};
|
||||
|
||||
function modificationIndex(app) {
|
||||
return app.inputManager.context.data('modification')
|
||||
}
|
||||
|
|
@ -10,60 +10,57 @@ function mergeInfo(opName, action) {
|
|||
return action;
|
||||
}
|
||||
|
||||
export const OperationActions = {
|
||||
|
||||
'CUT': mergeInfo('CUT', {
|
||||
info: 'makes a cut based on 2D sketch'
|
||||
}),
|
||||
|
||||
'PAD': mergeInfo('PAD', {
|
||||
info: 'extrudes 2D sketch'
|
||||
}),
|
||||
export const CUT = mergeInfo('CUT', {
|
||||
info: 'makes a cut based on 2D sketch'
|
||||
});
|
||||
|
||||
'REVOLVE': mergeInfo('REVOLVE', {
|
||||
info: 'revolve 2D sketch'
|
||||
}),
|
||||
export const PAD = mergeInfo('PAD', {
|
||||
info: 'extrudes 2D sketch'
|
||||
});
|
||||
|
||||
'SHELL': mergeInfo('SHELL', {
|
||||
info: 'makes shell using borders'
|
||||
}),
|
||||
|
||||
'BOX': mergeInfo('BOX', {
|
||||
info: 'creates new object box'
|
||||
}),
|
||||
export const REVOLVE = mergeInfo('REVOLVE', {
|
||||
info: 'revolve 2D sketch'
|
||||
});
|
||||
|
||||
'PLANE': mergeInfo('PLANE', {
|
||||
info: 'creates new object plane'
|
||||
}),
|
||||
|
||||
'SPHERE': mergeInfo('SPHERE', {
|
||||
info: 'creates new object sphere'
|
||||
}),
|
||||
export const SHELL = mergeInfo('SHELL', {
|
||||
info: 'makes shell using borders'
|
||||
});
|
||||
|
||||
'INTERSECTION': mergeInfo('INTERSECTION', {
|
||||
info: 'intersection operation on two solids'
|
||||
}),
|
||||
|
||||
'DIFFERENCE': mergeInfo('DIFFERENCE', {
|
||||
info: 'difference operation on two solids'
|
||||
}),
|
||||
|
||||
'UNION': mergeInfo('UNION', {
|
||||
info: 'union operation on two solids'
|
||||
}),
|
||||
|
||||
'IMPORT_STL': mergeInfo('IMPORT_STL', {
|
||||
info: 'import stl from external location'
|
||||
})
|
||||
};
|
||||
export const BOX = mergeInfo('BOX', {
|
||||
info: 'creates new object box'
|
||||
});
|
||||
|
||||
requiresFaceSelection(OperationActions.CUT, 1);
|
||||
requiresFaceSelection(OperationActions.PAD, 1);
|
||||
requiresFaceSelection(OperationActions.REVOLVE, 1);
|
||||
export const PLANE = mergeInfo('PLANE', {
|
||||
info: 'creates new object plane'
|
||||
});
|
||||
|
||||
requiresSolidSelection(OperationActions.INTERSECTION, 2);
|
||||
requiresSolidSelection(OperationActions.DIFFERENCE, 2);
|
||||
requiresSolidSelection(OperationActions.UNION, 2);
|
||||
export const SPHERE = mergeInfo('SPHERE', {
|
||||
info: 'creates new object sphere'
|
||||
});
|
||||
|
||||
export const INTERSECTION = mergeInfo('INTERSECTION', {
|
||||
info: 'intersection operation on two solids'
|
||||
});
|
||||
|
||||
export const DIFFERENCE = mergeInfo('DIFFERENCE', {
|
||||
info: 'difference operation on two solids'
|
||||
});
|
||||
|
||||
export const UNION = mergeInfo('UNION', {
|
||||
info: 'union operation on two solids'
|
||||
});
|
||||
|
||||
export const IMPORT_STL = mergeInfo('IMPORT_STL', {
|
||||
info: 'import stl from external location'
|
||||
});
|
||||
|
||||
requiresFaceSelection(CUT, 1);
|
||||
requiresFaceSelection(PAD, 1);
|
||||
requiresFaceSelection(REVOLVE, 1);
|
||||
|
||||
requiresSolidSelection(INTERSECTION, 2);
|
||||
requiresSolidSelection(DIFFERENCE, 2);
|
||||
requiresSolidSelection(UNION, 2);
|
||||
|
||||
function requiresFaceSelection(action, amount) {
|
||||
action.listens = ['selection'];
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import TabSwitcher from './ui/tab-switcher'
|
|||
import ControlBar from './ui/control-bar'
|
||||
import {InputManager} from './ui/input-manager'
|
||||
import {ActionManager} from './actions/actions'
|
||||
import * as CoreActions from './actions/core-actions'
|
||||
import {OperationActions} from './actions/operation-actions'
|
||||
import * as AllActions from './actions/all-actions'
|
||||
import Vector from '../math/vector'
|
||||
import {Matrix3, AXIS, ORIGIN, IDENTITY_BASIS} from '../math/l3space'
|
||||
import * as workbench from './workbench'
|
||||
|
|
@ -15,7 +14,7 @@ import * as math from '../math/math'
|
|||
import {IO} from '../sketcher/io'
|
||||
import {AddDebugSupport} from './debug'
|
||||
import {init as initSample} from './sample'
|
||||
import '../../css/app3d.less';
|
||||
import '../../css/app3d.less'
|
||||
|
||||
function App() {
|
||||
this.id = this.processHints();
|
||||
|
|
@ -24,8 +23,7 @@ function App() {
|
|||
this.inputManager = new InputManager(this);
|
||||
this.state = this.createState();
|
||||
this.viewer = new Viewer(this.bus, document.getElementById('viewer-container'));
|
||||
this.actionManager.registerActions(CoreActions);
|
||||
this.actionManager.registerActions(OperationActions);
|
||||
this.actionManager.registerActions(AllActions);
|
||||
this.tabSwitcher = new TabSwitcher($('#tab-switcher'), $('#view-3d'));
|
||||
this.controlBar = new ControlBar(this, $('#control-bar'));
|
||||
|
||||
|
|
@ -157,11 +155,15 @@ App.prototype.projectStorageKey = function(polyFaceId) {
|
|||
return App.STORAGE_PREFIX + this.id;
|
||||
};
|
||||
|
||||
App.prototype.sketchFace = function() {
|
||||
|
||||
App.prototype.sketchSelectedFace = function() {
|
||||
if (this.viewer.selectionMgr.selection.length == 0) {
|
||||
return;
|
||||
}
|
||||
var polyFace = this.viewer.selectionMgr.selection[0];
|
||||
};
|
||||
|
||||
App.prototype.sketchFace = function(polyFace) {
|
||||
var faceStorageKey = this.faceStorageKey(polyFace.id);
|
||||
|
||||
var savedFace = localStorage.getItem(faceStorageKey);
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ function setupBindings(bindings, bindingsDefinition, node) {
|
|||
const formattedValue = format(def.formatters, value);
|
||||
binder.apply(node, formattedValue, policy, def.key);
|
||||
});
|
||||
binder.init(node);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -132,10 +133,6 @@ function index(dom) {
|
|||
let bindingsDefinition = node.attr('data-bind');
|
||||
if (bindingsDefinition) {
|
||||
setupBindings(scope.bindings, bindingsDefinition, node)
|
||||
let template = node.text();
|
||||
if (template) {
|
||||
node.attr('data-bind-template', template);
|
||||
}
|
||||
}
|
||||
node.children().each((i, e) => queue.push($(e)))
|
||||
}
|
||||
|
|
@ -259,23 +256,32 @@ const DEFAULT_BINDER = {
|
|||
} else {
|
||||
node.show();
|
||||
}
|
||||
},
|
||||
init: (node) => {
|
||||
let template = node.text();
|
||||
if (template) {
|
||||
node.attr('data-bind-template', template);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const BINDERS = [
|
||||
{
|
||||
prefix: '@',
|
||||
apply: (node, value, policy, key) => node.attr(key, value)
|
||||
apply: (node, value, policy, key) => node.attr(key, value),
|
||||
init: (node) => {}
|
||||
},
|
||||
|
||||
{
|
||||
prefix: '$',
|
||||
apply: (node, value, policy, key) => node.css(key, value)
|
||||
apply: (node, value, policy, key) => node.css(key, value),
|
||||
init: (node) => {}
|
||||
},
|
||||
|
||||
{
|
||||
prefix: '!',
|
||||
apply: (node, value, policy, key) => value ? node.addClass(key) : node.removeClass(key)
|
||||
apply: (node, value, policy, key) => value ? node.addClass(key) : node.removeClass(key),
|
||||
init: (node) => {}
|
||||
},
|
||||
|
||||
DEFAULT_BINDER
|
||||
|
|
|
|||
|
|
@ -132,6 +132,12 @@ UI.prototype.registerWizard = function(wizard, overridingHistory) {
|
|||
};
|
||||
|
||||
wizard.focus();
|
||||
if (this.registeredWizard != undefined) {
|
||||
if (!this.registeredWizard.disposed) {
|
||||
this.registeredWizard.dispose();
|
||||
}
|
||||
}
|
||||
this.registeredWizard = wizard;
|
||||
return wizard;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ InputManager.prototype.handleActionClick = function(event) {
|
|||
this.clear();
|
||||
EventData.set(event, 'initiator', target);
|
||||
this.app.actionManager.run(action, event);
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,23 +12,14 @@ export function ModificationsPanel(app) {
|
|||
this.historyWizard = null;
|
||||
|
||||
this.app.bus.subscribe("craft", () => {
|
||||
let modifications = [];
|
||||
for (let i = 0; i < this.app.craft.history.length; i++) {
|
||||
let op = this.app.craft.history[i];
|
||||
let m = {
|
||||
id : i,
|
||||
info: this.app.ui.getInfoForOp(op),
|
||||
OnBind : (dom, data) => {
|
||||
dom.css('background-image', 'url('+ getIconForOp(op)+')');
|
||||
dom.click(() => this.app.craft.historyPointer = data.id);
|
||||
}
|
||||
};
|
||||
modifications.push(m);
|
||||
}
|
||||
Bind(this.dom, {modifications});
|
||||
this.updateList();
|
||||
this.updateHistoryPointer();
|
||||
});
|
||||
|
||||
this.app.bus.subscribe("historyShrink", () => {
|
||||
this.updateList();
|
||||
});
|
||||
|
||||
this.app.bus.subscribe("refreshSketch", () => {
|
||||
if (this.historyWizard != null) {
|
||||
var craft = this.app.craft;
|
||||
|
|
@ -42,6 +33,25 @@ export function ModificationsPanel(app) {
|
|||
Bind(this.dom, {});
|
||||
}
|
||||
|
||||
ModificationsPanel.prototype.updateList = function() {
|
||||
let modifications = [];
|
||||
for (let i = 0; i < this.app.craft.history.length; i++) {
|
||||
let op = this.app.craft.history[i];
|
||||
let m = {
|
||||
id : i,
|
||||
info: this.app.ui.getInfoForOp(op),
|
||||
OnBind : (dom, data) => {
|
||||
dom.css('background-image', 'url('+ getIconForOp(op)+')');
|
||||
if (!op.face) {
|
||||
dom.find('.require-face').addClass('action-disabled');
|
||||
}
|
||||
}
|
||||
};
|
||||
modifications.push(m);
|
||||
}
|
||||
Bind(this.dom, {modifications});
|
||||
};
|
||||
|
||||
ModificationsPanel.prototype.updateHistoryPointer = function() {
|
||||
if (this.historyWizard != null) {
|
||||
this.historyWizard.dispose();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,14 @@
|
|||
<div class="tc-folder">
|
||||
<div class="tc-row tc-title">Modifications</div>
|
||||
<div class="tc-list" data-bind-list="modifications">
|
||||
<div class="tc-row tc-pseudo-btn modification-item" data-bind="info"></div>
|
||||
<div class="tc-row tc-pseudo-btn modification-item context-hover action-item" data-action="SetHistoryPointer" data-bind="@data-modification: id">
|
||||
<span data-bind="info" style="float: left"></span>
|
||||
<span style="float: right" class="modification-right-buttons">
|
||||
<i class="fa fa-edit modification-button action-item" data-action="OpenHistoryWizard"></i>
|
||||
<i class="fa fa-image modification-button action-item require-face" data-action="EditOperationSketch"></i>
|
||||
<i class="fa fa-remove modification-button action-item danger" data-action="RemoveModification"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tc-row tc-ctrl tc-buttons-block">
|
||||
<span class="tc-block-btn active-btn">Finish History Editing</span>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export function Wizard(viewer, initParams) {
|
|||
this.viewer = viewer;
|
||||
this.disposed = false;
|
||||
this.ui = {
|
||||
box: new tk.Box(),
|
||||
box: new tk.Box($('#view-3d')),
|
||||
folder: new tk.Folder(this.title())
|
||||
};
|
||||
tk.add(this.ui.box, this.ui.folder);
|
||||
|
|
|
|||
|
|
@ -709,6 +709,17 @@ export function Craft(app) {
|
|||
});
|
||||
}
|
||||
|
||||
Craft.prototype.remove = function(modificationIndex) {
|
||||
const history = this.history;
|
||||
history.splice(modificationIndex, history.length - modificationIndex);
|
||||
|
||||
if (this.historyPointer >= history.length) {
|
||||
this.finishHistoryEditing();
|
||||
} else {
|
||||
this.app.bus.notify('historyShrink');
|
||||
}
|
||||
};
|
||||
|
||||
Craft.prototype.loadHistory = function(history) {
|
||||
this.history = history;
|
||||
this._historyPointer = history.length;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ export function methodRef(_this, methodName, args) {
|
|||
};
|
||||
}
|
||||
|
||||
export function Box() {
|
||||
export function Box(parent) {
|
||||
this.root = this.content = $('<div class="tc-box" />');
|
||||
this.root.addClass('tc-box tc-scroll');
|
||||
this.root.appendTo('body');
|
||||
this.root.appendTo(parent ? parent : 'body');
|
||||
}
|
||||
|
||||
Box.prototype.close = function() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ body {
|
|||
.main-font;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.main-font {
|
||||
font: 11px 'Lucida Grande', sans-serif;
|
||||
}
|
||||
|
|
@ -193,6 +197,10 @@ body {
|
|||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.action-disabled {
|
||||
color: @suppressed-color - 30;
|
||||
}
|
||||
|
||||
.menu-item.action-disabled {
|
||||
color: @suppressed-color;
|
||||
}
|
||||
|
|
@ -251,4 +259,33 @@ body {
|
|||
.modification-item {
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.modification-button {
|
||||
line-height: 27px;
|
||||
font-size: 1.3em;
|
||||
padding: 0 3px 0 3px;
|
||||
&:hover {
|
||||
color: yellowgreen;
|
||||
}
|
||||
}
|
||||
|
||||
.modification-button.danger {
|
||||
&:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.modification-button.action-disabled {
|
||||
&:hover {
|
||||
.action-disabled;
|
||||
}
|
||||
}
|
||||
|
||||
.modification-right-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.history-selected .modification-right-buttons, .modification-item:hover .modification-right-buttons {
|
||||
display: initial;
|
||||
}
|
||||
Loading…
Reference in a new issue