Feature Request: Model history buttons for each item. #21

This commit is contained in:
Val Erastov 2016-12-25 02:54:48 -08:00
parent 061465385d
commit ac7f52325f
14 changed files with 218 additions and 82 deletions

View file

@ -0,0 +1,3 @@
export * from './core-actions'
export * from './operation-actions'
export * from './history-actions'

View file

@ -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'
};

View 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')
}

View file

@ -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'];

View file

@ -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);

View file

@ -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

View file

@ -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;
};

View file

@ -76,6 +76,7 @@ InputManager.prototype.handleActionClick = function(event) {
this.clear();
EventData.set(event, 'initiator', target);
this.app.actionManager.run(action, event);
event.stopPropagation();
}
};

View file

@ -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();

View file

@ -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>

View file

@ -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);

View file

@ -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;

View file

@ -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() {

View file

@ -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;
}