var Toolbars, BarItems, MenuBar, open_menu, Toolbox; //Bars class MenuSeperator { constructor() { this.menu_node = $('') } } class BarItem { constructor(data) { this.id = data.id; if (!data.private) { BarItems[this.id] = this; } this.name = tl('action.'+this.id) this.description = tl('action.'+this.id+'.desc') if (data.name) this.name = tl(data.name); if (data.description) this.description = tl(data.description); this.node; this.condition = data.condition; this.nodes = [] } conditionMet() { if (this.condition === undefined) { return true; } else if (typeof this.condition === 'function') { return this.condition() } else { return !!this.condition } } addLabel(in_bar) { $(this.node).attr('title', this.description) if (in_bar) { $(this.node).prepend('') } else { $(this.node).append('
'+this.name+'
') .on('mouseenter', function() { var tooltip = $(this).find('div.tooltip') if (!tooltip || typeof tooltip.offset() !== 'object') return; //Left if (tooltip.css('left') === '-4px') { tooltip.css('left', 'auto') } if (-tooltip.offset().left > 4) { tooltip.css('left', '-4px') } //Right if (tooltip.css('right') === '-4px') { tooltip.css('right', 'auto') } if ((tooltip.offset().left + tooltip.width()) - $(window).width() > 4) { tooltip.css('right', '-4px') } }) } } getNode() { var scope = this; if (this.id === 'uv_rotation') { } if (scope.nodes.length === 0) { scope.nodes = [scope.node] } if (!scope.node.isConnected) { $(scope.node).detach() return scope.node; } var i = 0; while (i < scope.nodes.length) { if (!scope.nodes[i].isConnected) { $(scope.nodes[i]).detach() return scope.nodes[i]; } i++; } var clone = $(scope.node).clone(true, true).get(0); scope.nodes.push(clone); return clone; } toElement(destination) { $(destination).append(this.node) return this; } } class KeybindItem { constructor(data) { this.id = data.id this.type = 'keybind_item' this.name = tl('keybind.'+this.id) this.category = data.category ? data.category : 'misc' if (data.keybind) { this.default_keybind = data.keybind } this.keybind = new Keybind(data.keybind).set(Keybinds.stored[this.id]) Keybinds.actions.push(this) Keybinds.extra[this.id] = this; this.keybind.setAction(this.id) } } class Action extends BarItem { constructor(data) { super(data) var scope = this; this.type = 'action' this.category = data.category ? data.category : 'misc' //Key if (data.keybind) { this.default_keybind = data.keybind } if (Keybinds.stored[this.id]) { this.keybind = new Keybind(Keybinds.stored[this.id]) } else { this.keybind = new Keybind(data.keybind) } this.keybind.setAction(this.id) this.work_in_dialog = data.work_in_dialog === true //Icon this.icon = data.icon this.color = data.color if (data.linked_setting) { this.description = tl('settings.'+data.linked_setting+'.desc') this.linked_setting = data.linked_setting } if (data.condition) this.condition = data.condition //Node this.click = data.click this.icon_node = Blockbench.getIconNode(this.icon, this.color) this.node = $(`
`).get(0) this.nodes = [this.node] this.addLabel(data.label) this.menu_node = $(`
  • ${this.name}
  • `).get(0) $(this.node).add(this.menu_node).prepend(this.icon_node) $(this.node).click(function(e) {scope.trigger(e)}) if (data.linked_setting) { this.toggleLinkedSetting(false) } Keybinds.actions.push(this) } trigger(event) { var scope = this; if (BARS.condition(scope.condition, scope)) { scope.click(event) $(scope.nodes).each(function() { $(this).css('color', 'var(--color-light)') }) setTimeout(function() { $(scope.nodes).each(function() { $(this).css('color', '') }) }, 200) return true; } return false; } setIcon(icon) { var scope = this; this.icon = icon this.icon_node = Blockbench.getIconNode(this.icon) $(this.menu_node).find('.icon').replaceWith(this.icon_node) this.nodes.forEach(function(n) { $(n).find('.icon').replaceWith($(scope.icon_node).clone()) }) } toggleLinkedSetting(change) { if (this.linked_setting && settings[this.linked_setting]) { if (change !== false) { settings[this.linked_setting].value = !settings[this.linked_setting].value } this.setIcon(settings[this.linked_setting].value ? 'check_box' : 'check_box_outline_blank') } } } class Tool extends Action { constructor(data) { super(data) var scope = this; this.type = 'tool' this.toolbar = data.toolbar; this.selectFace = data.selectFace; this.paintTool = data.paintTool; this.transformerMode = data.transformerMode; this.allowWireframe = data.allowWireframe == true; this.onCanvasClick = data.onCanvasClick; this.onSelect = data.onSelect; this.onUnselect = data.onUnselect; $(this.node).click(function() {scope.select()}) } trigger() { if (BARS.condition(this.condition, this)) { this.select() return true; } return false; } select() { if (this === Toolbox.selected) return; if (Toolbox.selected && Toolbox.selected.onUnselect && typeof Toolbox.selected.onUnselect == 'function') { Toolbox.selected.onUnselect() } Toolbox.selected = this; if (this.transformerMode) { Transformer.setMode(this.transformerMode) } if (Prop.wireframe && !this.allowWireframe) { Prop.wireframe = false Canvas.updateAll() } if (this.toolbar && Toolbars[this.toolbar]) { Toolbars[this.toolbar].toPlace('tool_options') resizeWindow() } else { $('.toolbar_wrapper.tool_options > .toolbar').detach() } $('#preview').css('cursor', (this.cursor ? this.cursor : 'default')) updateSelection() $('.tool.sel').removeClass('sel') $('.tool.'+this.id).addClass('sel') if (typeof this.onSelect == 'function') { this.onSelect() } return this; } } class Widget extends BarItem { constructor(data) { super(data) this.type = 'widget' } } class NumSlider extends Widget { constructor(data) { super(data); this.uv = !!data.uv; this.type = 'numslider' this.icon = 'code' this.value = 0; this.width = 79; if (typeof data.get === 'function') this.get = data.get; this.onBefore = data.onBefore; this.onAfter = data.onAfter; if (typeof data.change === 'function') this.change = data.change; if (data.settings) { this.settings = data.settings; if (this.settings.default) { this.value = this.settings.default } if (data.settings.interval) { this.getInterval = data.settings.interval } else { this.getInterval = function(event) { return this.settings.step ? this.settings.step : 1 }; } } else { this.getInterval = function(event) { return canvasGridSize(event.shiftKey, event.ctrlKey); }; } if (typeof data.getInterval === 'function') { this.getInterval = data.getInterval; } var scope = this; this.node = $( `
    ${this.name}
    `).get(0); this.jq_outer = $(this.node) this.jq_inner = this.jq_outer.find('.nslide'); //Slide this.jq_inner.draggable({ revert: true, axis: 'x', revertDuration: 0, helper: function () {return '
    '}, opacity: 0.8, appendTo: 'body', cursor: "none", start: function(event, ui) { if (typeof scope.onBefore === 'function') { scope.onBefore() } scope.pre = canvasGridSize() scope.top = ui.position.top scope.left = ui.position.left scope.last_value = scope.value }, drag: function(event, ui) { scope.slide(event, ui) }, stop: function() { if (typeof scope.onAfter === 'function') { scope.onAfter(scope.value - scope.last_value) } } }) //Input .keypress(function (e) { if (e.keyCode === 10 || e.keyCode === 13) { e.preventDefault(); scope.stopInput() } }) .keyup(function (e) { if (e.keyCode !== 10 && e.keyCode !== 13) { scope.input() } }) .focusout(function() { scope.stopInput() }) .click(function(event) { if (event.target != this) return; scope.jq_inner.find('.nslide_arrow').remove() scope.jq_inner.attr('contenteditable', 'true') scope.jq_inner.addClass('editing') scope.jq_inner.focus() document.execCommand('selectAll') }); //Arrows this.jq_outer .on('mouseenter', function() { scope.jq_outer.append( '
    navigate_before
    '+ '
    navigate_next
    ' ) var n = limitNumber(scope.width/2-24, 6, 1000) scope.jq_outer.find('.nslide_arrow.na_left').click(function(e) { scope.arrow(-1, e) }).css('margin-left', (-n-24)+'px') scope.jq_outer.find('.nslide_arrow.na_right').click(function(e) { scope.arrow(1, e) }).css('margin-left', n+'px') }) .on('mouseleave', function() { scope.jq_outer.find('.nslide_arrow').remove() }) } setWidth(width) { if (width) { this.width = width } else { width = this.width } $(this.node).width(width).find('> div.nslide').css('width', width+'px') return this; } slide(event, ui) { //Variables var scope = this; var number = 0; //Math var offset = Math.round((event.clientX-scope.left)/50) if (scope.uv === false) { offset *= canvasGridSize(); } var difference = offset - scope.pre; scope.pre = offset; difference *= this.getInterval(event) if (difference == 0 || isNaN(difference)) return; this.change(difference) this.update() } input(obj) { var scope = this; if (typeof this.onBefore === 'function') { this.onBefore() } this.last_value = this.value var number = this.jq_inner.text().replace(/[^-.0-9]/g, ""); var number = parseFloat(number) if (isNaN(number)) { number = 0; } this.change(number, true) this.update() if (typeof this.onAfter === 'function') { this.onAfter(scope.value - scope.last_value) } } stopInput() { this.jq_inner.attr('contenteditable', 'false') this.jq_inner.removeClass('editing') this.update() } arrow(difference, event) { if (typeof this.onBefore === 'function') { this.onBefore() } difference *= this.getInterval(event) this.change(difference) this.update() if (typeof this.onAfter === 'function') { this.onAfter(difference) } } setValue(value, trim) { if (typeof value === 'string') { value = parseFloat(value) } if (trim === false) { this.value = value } else if (typeof value === 'number') { this.value = trimFloatNumber(value) } else { } this.jq_outer.find('.nslide:not(.editing)').text(this.value) //this.jq_inner.text(this.value) return this; } change(difference, fixed) { //Solo Sliders only var num = difference if (!fixed) { num += this.get() } if (this.settings && typeof this.settings.min === 'number') { num = limitNumber(num, this.settings.min, this.settings.max) } this.value = num } get() { //Solo Sliders only return parseFloat(this.value); } update() { var number = this.get(); this.setValue(number) $('#nslide_head #nslide_offset').text(this.name+': '+number) } } class BarSlider extends Widget { constructor(data) { super(data) var scope = this; this.type = 'slider' this.icon = 'fa-sliders' this.value = data.value||0 this.node = $('
    '+ ''+ '
    ').get(0) this.addLabel(data.label) if (typeof data.onChange === 'function') { this.onChange = data.onChange } $(this.node).children('input').on('input', function(event) { scope.change(event) }) } change(event) { this.set( parseFloat( $(event.target).val() ) ) if (this.onChange) { this.onChange(this, event) } } set(value) { this.value = value $(this.nodes).children('input').val(value) } get() { return this.value } } class BarSelect extends Widget { constructor(data) { super(data) var scope = this; this.type = 'select' this.icon = 'list' this.node = $('
    ').get(0) if (data.width) { $(this.node).children('select').css('width', data.width+'px') } var select = $(this.node).find('select') if (data.options) { for (var key in data.options) { if (data.options.hasOwnProperty(key)) { if (!this.value) { this.value = key } var name = data.options[key] if (name === true) { name = tl('action.'+this.id+'.'+key) } select.append('') } } } this.addLabel(data.label) if (typeof data.onChange === 'function') { this.onChange = data.onChange } $(this.node).children('select').change(function(event) { scope.change(event) }) } change(event) { this.set( $(event.target).find('option:selected').attr('id') ) if (this.onChange) { this.onChange(this, event) } } set(id) { this.value = id $(this.nodes).find('option#'+id).attr('selected', true).siblings().attr('selected', false) } get() { return this.value } } class BarText extends Widget { constructor(data) { super(data) this.type = 'bar_text' this.icon = 'text_format' this.node = $('
    '+data.text||''+'
    ').get(0) if (data.right) { $(this.node).addClass('f_right') } if (typeof data.click === 'function') { this.click = data.click; this.node.addEventListener('click', this.click) } } set(text) { $(this.nodes).text(text) } } class ColorPicker extends Widget { constructor(data) { super(data) var scope = this; this.type = 'color_picker' this.icon = 'color_lens' this.node = $('
    ').get(0) this.addLabel(data.label) this.jq = $(this.node).find('input') if (typeof data.onChange === 'function') { this.onChange = data.onChange } this.value = new tinycolor('ffffff') this.jq.spectrum({ preferredFormat: "hex", color: 'ffffff', showAlpha: true, showInput: true, maxSelectionSize: 128, showPalette: data.palette === true, palette: data.palette ? [] : undefined, show: function() { open_interface = scope }, hide: function() { open_interface = false }, change: function(c) { scope.change(c) } }) } change(color) { if (this.onChange) { this.onChange() } } hide() { this.jq.spectrum('cancel'); } confirm() { this.jq.spectrum('hide'); } set(color) { this.value = new tinycolor(color) this.jq.spectrum('set', this.value.toHex8String()) return this; } get() { this.value = this.jq.spectrum('get'); return this.value; } } class Toolbar { constructor(data) { var scope = this; this.children = []; this.default_children = data.children.slice() this.node = $('
    '+ '
    '+ '
    more_vert
    '+tl('data.toolbar')+'
    '+ '
    ').get(0) $(this.node).find('div.toolbar_menu').click(function(event) {scope.contextmenu(event)}) if (data) { this.id = data.id this.narrow = !!data.narrow this.build(data) } } build(data, force) { var scope = this; //Items this.children.length = 0; var items = data.children if (!force && BARS.stored[scope.id] && typeof BARS.stored[scope.id] === 'object') { items = BARS.stored[scope.id] } if (items && items.constructor.name === 'Array') { var content = $(scope.node).find('div.content') content.children().detach() items.forEach(function(id) { if (typeof id === 'string' && id.substr(0, 1) === '_') { content.append('
    ') scope.children.push('_'+guid().substr(0,8)) return; } var item = BarItems[id] if (item) { scope.children.push(item) if (BARS.condition(item.condition)) { content.append(item.getNode()) } } }) } $(scope.node).toggleClass('narrow', this.narrow) if (data.default_place) { this.toPlace(this.id) } return this; } contextmenu(event) { this.menu.open(event, this) } editMenu() { var scope = this; BARS.editing_bar = this; this.children.forEach(function(c, ci) { }) BARS.list.currentBar = this.children; showDialog('toolbar_edit'); return this; } add(action, position) { if (position === undefined) position = this.children.length this.children.splice(position, 0, action) this.update() return this; } remove(action) { var i = this.children.length-1; while (i >= 0) { var item = this.children[i] if (item === action || item.id === action) { this.children.splice(i, 1) this.update() return this; } i--; } return this; } update() { var scope = this; var content = $(this.node).find('.content') content.find('> .tool').detach() var seperators = content.find('> .toolbar_seperator').detach() var sep_nr = 0; this.children.forEach(function(item, i) { if (typeof item === 'string') { var last = content.find('> :last-child') if (last.length === 0 || last.hasClass('toolbar_seperator') || i == scope.children.length-1) { return } var sep = seperators[sep_nr] if (sep) { content.append(sep) sep_nr++; } else { content.append('
    ') } } else if (!BARS.condition( item.condition )) { } else { content.append(item.getNode()) } }) var last = content.find('> :last-child') if (last.length && last.hasClass('toolbar_seperator')) { last.remove() } this.save() return this; } toPlace(place) { if (!place) place = this.id $('div.toolbar_wrapper.'+place+' > .toolbar').detach() $('div.toolbar_wrapper.'+place).append(this.node) return this; } save() { var arr = [] this.children.forEach(function(c) { if (typeof c === 'string') { arr.push(c) } else { arr.push(c.id) } }) BARS.stored[this.id] = arr localStorage.setItem('toolbars', JSON.stringify(BARS.stored)) return this; } reset() { this.build({ children: this.default_children, default_place: this.default_place }, true) this.save() return this; } } var BARS = { stored: {}, editing_bar: undefined, condition: function(condition, context) { if (condition === undefined) { return true; } else if (typeof condition === 'function') { return condition(context) } else { return !!condition } }, setupActions: function() { BarItems = {} //Extras new KeybindItem({ id: 'preview_select', category: 'navigate', keybind: new Keybind({key: 1, ctrl: null, shift: null, alt: null}) }) new KeybindItem({ id: 'preview_rotate', category: 'navigate', keybind: new Keybind({key: 1}) }) new KeybindItem({ id: 'preview_drag', category: 'navigate', keybind: new Keybind({key: 3}) }) new KeybindItem({ id: 'confirm', category: 'navigate', keybind: new Keybind({key: 13}) }) new KeybindItem({ id: 'cancel', category: 'navigate', keybind: new Keybind({key: 27}) }) //Sliders function moveOnAxis(value, fixed, axis) { selected.forEach(function(obj, i) { var number = value if (fixed) { number -= obj.from[axis] } number = limitToBox(obj.to [axis] + number) - obj.to [axis]; number = limitToBox(obj.from[axis] + number) - obj.from[axis]; obj.from[axis] += number obj.to[axis] += number obj.mapAutoUV() }) Canvas.updatePositions() } new NumSlider({ id: 'slider_pos_x', condition: function() {return selected.length}, get: function() { return selected[0].from[0] }, change: function(value, fixed) { moveOnAxis(value, fixed, 0) }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('move') } }) new NumSlider({ id: 'slider_pos_y', condition: function() {return selected.length}, get: function() { return selected[0].from[1] }, change: function(value, fixed) { moveOnAxis(value, fixed, 1) }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('move') } }) new NumSlider({ id: 'slider_pos_z', condition: function() {return selected.length}, get: function() { return selected[0].from[2] }, change: function(value, fixed) { moveOnAxis(value, fixed, 2) }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('move') } }) function scaleOnAxis(value, fixed, axis) { selected.forEach(function(obj, i) { var diff = value if (fixed) { diff -= obj.size(axis) } obj.to[axis] = limitToBox(obj.to[axis] + diff) obj.mapAutoUV() }) Canvas.updatePositions() } new NumSlider({ id: 'slider_size_x', condition: function() {return selected.length}, get: function() { return selected[0].to[0] - selected[0].from[0] }, change: function(value, fixed) { scaleOnAxis(value, fixed, 0) }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('resize') } }) new NumSlider({ id: 'slider_size_y', condition: function() {return selected.length}, get: function() { return selected[0].to[1] - selected[0].from[1] }, change: function(value, fixed) { scaleOnAxis(value, fixed, 1) }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('resize') } }) new NumSlider({ id: 'slider_size_z', condition: function() {return selected.length}, get: function() { return selected[0].to[2] - selected[0].from[2] }, change: function(value, fixed) { scaleOnAxis(value, fixed, 2) }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('resize') } }) new NumSlider({ id: 'slider_inflate', condition: function() {return Blockbench.entity_mode && selected.length}, get: function() { return selected[0].inflate }, change: function(value, fixed) { selected.forEach(function(obj, i) { var diff = value if (fixed) { diff -= obj.inflate } obj.inflate = obj.inflate + diff }) Canvas.updatePositions() }, onBefore: function() { Undo.initEdit({cubes: selected}) }, onAfter: function() { Undo.finishEdit('inflate') } }) function rotateOnAxis(value, fixed, axis) { if (Blockbench.entity_mode) { if (!selected_group) return; if (!fixed) { value = value + selected_group.rotation[axis] } value = value % 360 selected_group.rotation[axis] = value Canvas.updatePositions() return; } //Warning if (settings.limited_rotation.value && settings.dialog_rotation_limit.value) { var i = 0; while (i < selected.length) { if (selected[i].rotation[(axis+1)%3] || selected[i].rotation[(axis+2)%3] ) { i = Infinity Blockbench.showMessageBox({ title: tl('message.rotation_limit.title'), icon: 'rotate_right', message: tl('message.rotation_limit.message'), buttons: [tl('dialog.ok'), tl('dialog.dontshowagain')] }, function(r) { if (r === 1) { settings.dialog_rotation_limit.value = false saveSettings() } }) return; //Gotta stop the numslider here } i++; } } var origin = selected[0].origin selected.forEach(function(obj, i) { if (!obj.rotation.equals([0,0,0])) { origin = obj.origin } }) selected.forEach(function(obj, i) { if (obj.rotation.equals([0,0,0])) { obj.origin = origin } var obj_val = value; if (!fixed) { obj_val += obj.rotation[axis] } obj_val = obj_val % 360 if (settings.limited_rotation.value) { //Limit To 1 Axis obj.rotation[(axis+1)%3] = 0 obj.rotation[(axis+2)%3] = 0 //Limit Angle obj_val = Math.round(obj_val/22.5)*22.5 if (obj_val > 45 || obj_val < -45) { let f = obj_val > 45 obj.roll(axis, f!=(axis==1) ? 1 : 3) obj_val = f ? -22.5 : 22.5; } } obj.rotation[axis] = obj_val }) Canvas.updatePositions() } function getRotationInterval(event) { if (settings.limited_rotation.value && !Blockbench.entity_mode) { return 22.5; } else if (event.shiftKey && event.ctrlKey) { return 0.25; } else if (event.shiftKey) { return 45; } else if (event.ctrlKey) { return 1; } else { return 5; } } new NumSlider({ id: 'slider_rotation_x', condition: function() {return !!(Blockbench.entity_mode ? selected_group : selected.length)}, get: function() { var obj = Blockbench.entity_mode ? selected_group : selected[0] return obj ? obj.rotation[0] : '' }, change: function(value, fixed) { rotateOnAxis(value, fixed, 0) }, onBefore: function() { Undo.initEdit({cubes: selected, group: selected_group}) }, onAfter: function() { Undo.finishEdit('rotate') }, getInterval: getRotationInterval }) new NumSlider({ id: 'slider_rotation_y', condition: function() {return !!(Blockbench.entity_mode ? selected_group : selected.length)}, get: function() { var obj = Blockbench.entity_mode ? selected_group : selected[0] return obj ? obj.rotation[1] : '' }, change: function(value, fixed) { rotateOnAxis(value, fixed, 1) }, onBefore: function() { Undo.initEdit({cubes: selected, group: selected_group}) }, onAfter: function() { Undo.finishEdit('rotate') }, getInterval: getRotationInterval }) new NumSlider({ id: 'slider_rotation_z', condition: function() {return !!(Blockbench.entity_mode ? selected_group : selected.length)}, get: function() { var obj = Blockbench.entity_mode ? selected_group : selected[0] return obj ? obj.rotation[2] : '' }, change: function(value, fixed) { rotateOnAxis(value, fixed, 2) }, onBefore: function() { Undo.initEdit({cubes: selected, group: selected_group}) }, onAfter: function() { Undo.finishEdit('rotate') }, getInterval: getRotationInterval }) function moveOriginOnAxis(value, fixed, axis) { if (selected_group) { var diff = value if (fixed) { diff -= selected_group.origin[axis] } selected_group.origin[axis] += diff Canvas.updatePositions() return; } selected.forEach(function(obj, i) { var diff = value if (fixed) { diff -= obj.origin[axis] } obj.origin[axis] += diff }) Canvas.updatePositions() } new NumSlider({ id: 'slider_origin_x', condition: function() {return !!(Blockbench.entity_mode ? selected_group : selected.length)}, get: function() { var obj = Blockbench.entity_mode ? selected_group : selected[0] return obj ? obj.origin[0] : '' }, change: function(value, fixed) { moveOriginOnAxis(value, fixed, 0) }, onBefore: function() { Undo.initEdit({cubes: selected, group: selected_group}) }, onAfter: function() { Undo.finishEdit('origin') } }) new NumSlider({ id: 'slider_origin_y', condition: function() {return !!(Blockbench.entity_mode ? selected_group : selected.length)}, get: function() { var obj = Blockbench.entity_mode ? selected_group : selected[0] return obj ? obj.origin[1] : '' }, change: function(value, fixed) { moveOriginOnAxis(value, fixed, 1) }, onBefore: function() { Undo.initEdit({cubes: selected, group: selected_group}) }, onAfter: function() { Undo.finishEdit('origin') } }) new NumSlider({ id: 'slider_origin_z', condition: function() {return !!(Blockbench.entity_mode ? selected_group : selected.length)}, get: function() { var obj = Blockbench.entity_mode ? selected_group : selected[0] return obj ? obj.origin[2] : '' }, change: function(value, fixed) { moveOriginOnAxis(value, fixed, 2) }, onBefore: function() { Undo.initEdit({cubes: selected, group: selected_group}) }, onAfter: function() { Undo.finishEdit('origin') } }) //Brush new BarSelect({ id: 'vertex_snap_mode', options: { move: true, scale: true } }) new ColorPicker({ id: 'brush_color', palette: true }) new BarSelect({ id: 'brush_mode', options: { brush: true, noise: true, eraser: true, fill: true } }) new NumSlider({ id: 'slider_brush_size', settings: { min: 1, max: 20, step: 1, default: 1, } }) new NumSlider({ id: 'slider_brush_softness', settings: { min: 0, max: 100, default: 0, interval: function(event) { if (event.shiftKey && event.ctrlKey) { return 0.25; } else if (event.shiftKey) { return 5; } else if (event.ctrlKey) { return 1; } else { return 10; } } } }) new NumSlider({ id: 'slider_brush_opacity', settings: { min: 0, max: 100, default: 100, interval: function(event) { if (event.shiftKey && event.ctrlKey) { return 0.25; } else if (event.shiftKey) { return 5; } else if (event.ctrlKey) { return 1; } else { return 10; } } } }) //Tools new Tool({ id: 'move_tool', icon: 'fa-hand-paper-o', category: 'tools', selectFace: true, allowWireframe: true, transformerMode: 'translate', toolbar: 'transform', keybind: new Keybind({key: 86}) }) new Tool({ id: 'resize_tool', icon: 'open_with', category: 'tools', selectFace: true, allowWireframe: true, transformerMode: 'scale', toolbar: 'transform', keybind: new Keybind({key: 83}) }) new Tool({ id: 'brush_tool', icon: 'fa-paint-brush', category: 'tools', toolbar: 'brush', selectFace: true, transformerMode: 'hidden', paintTool: true, keybind: new Keybind({key: 66}), onCanvasClick: function(data) { Painter.startBrushCanvas(data, data.event) }, onSelect: function() { BarItems.slider_brush_size.update() BarItems.slider_brush_softness.update() BarItems.slider_brush_opacity.update() $('.UVEditor').find('#uv_size').hide() }, onUnselect: function() { $('.UVEditor').find('#uv_size').show() } }) new Tool({ id: 'vertex_snap_tool', icon: 'icon-vertexsnap', transformerMode: 'hidden', toolbar: 'vertex_snap', category: 'tools', select_cubes: true, cursor: 'copy', keybind: new Keybind({key: 88}), onCanvasClick: function(data) { Vertexsnap.canvasClick(data) }, onSelect: function() { Blockbench.addListener('update_selection', Vertexsnap.select) Vertexsnap.select() }, onUnselect: function() { Vertexsnap.removeVertexes() Vertexsnap.step1 = true Blockbench.removeListener('update_selection', Vertexsnap.select) } }) new Tool({ id: 'display_mode_tool', icon: 'icon-player', transformerMode: 'hidden', category: 'tools', select_cubes: false, condition: function() {return !Blockbench.entity_mode}, onCanvasClick: function(data) { }, onSelect: function() { enterDisplaySettings() }, onUnselect: function() { exitDisplaySettings() } }) new Action({ id: 'swap_tools', icon: 'swap_horiz', category: 'tools', keybind: new Keybind({key: 32}), click: function () { if (Toolbox.selected.id === 'move_tool') { BarItems.resize_tool.select() } else if (Toolbox.selected.id === 'resize_tool') { BarItems.move_tool.select() } } }) //File new Action({ id: 'project_window', icon: 'featured_play_list', category: 'file', click: function () {showDialog('project_settings');} }) new Action({ id: 'open_model_folder', icon: 'folder_open', category: 'file', condition: () => {return isApp && Prop.file_path && Prop.file_path !== ''}, click: function () { shell.showItemInFolder(Prop.file_path) } }) new Action({ id: 'new_block_model', icon: 'insert_drive_file', category: 'file', keybind: new Keybind({key: 78, ctrl: true}), click: function () {newProject()} }) new Action({ id: 'new_entity_model', icon: 'pets', category: 'file', keybind: new Keybind({key: 78, ctrl: true, shift: true}), click: function () {newProject(true)} }) new Action({ id: 'open_model', icon: 'assessment', category: 'file', keybind: new Keybind({key: 79, ctrl: true}), click: function () { Blockbench.import({ extensions: ['json', 'jem', 'jpm'], type: 'JSON Model' }, function(files) { if (isApp) { addRecentProject({name: pathToName(files[0].path, 'mobs_id'), path: files[0].path}) } loadModel(files[0].content, files[0].path) }) } }) new Action({ id: 'add_model', icon: 'assessment', category: 'file', click: function () { Blockbench.import({ extensions: ['json', 'jem', 'jpm'], type: 'JSON Model' }, function(files) { if (isApp) { addRecentProject({name: pathToName(files[0].path, 'mobs_id'), path: files[0].path}) } loadModel(files[0].content, files[0].path, true) }) } }) new Action({ id: 'extrude_texture', icon: 'eject', category: 'file', click: function () { Blockbench.import({ extensions: ['png'], type: 'PNG Texture', readtype: 'image' }, function(files) { if (files.length) { if (isApp) { new Texture().fromPath(files[0].path).add().fillParticle() } else { new Texture().fromDataURL(files[0].content).add().fillParticle() } showDialog('image_extruder') Extruder.drawImage(isApp ? files[0].path : files[0].content) } }) } }) new Action({ id: 'export_blockmodel', icon: 'insert_drive_file', category: 'file', keybind: new Keybind({key: 83, ctrl: true, shift: true}), condition: function() {return !Blockbench.entity_mode}, click: function () { var content = buildBlockModel() Blockbench.export({ type: 'JSON Model', extensions: ['json'], name: Project.name||'model', startpath: Prop.file_path, content: content }) } }) new Action({ id: 'export_entity', icon: 'pets', category: 'file', keybind: new Keybind({key: 83, ctrl: true, shift: true}), condition: function() {return Blockbench.entity_mode}, click: function () { var content = buildEntityModel({raw: true}); Blockbench.export({ type: 'JSON Entity Model', extensions: ['json'], name: Project.name, startpath: Prop.file_path, content: content, custom_writer: writeFileEntity }) } }) new Action({ id: 'export_optifine_part', icon: 'icon-optifine_file', category: 'file', condition: function() {return !Blockbench.entity_mode}, click: function () { var content = buildJPMModel() Blockbench.export({ type: 'Optifine Part Model', extensions: ['jpm'], name: Project.name, startpath: Prop.file_path, content: content }) } }) new Action({ id: 'export_optifine_full', icon: 'icon-optifine_file', category: 'file', condition: function() {return Blockbench.entity_mode}, click: function () { var content = buildJEMModel() Blockbench.export({ type: 'Optifine Entity Model', extensions: ['jem'], name: Project.name, startpath: Prop.file_path, content: content }) } }) new Action({ id: 'export_obj', icon: 'icon-objects', category: 'file', click: function () { Blockbench.export({ type: 'Alias Wavefront', extensions: ['obj'], name: Project.name ? Project.name : Prop.file_name, startpath: Prop.file_path, custom_writer: writeFileObj }) } }) new Action({ id: 'save', icon: 'save', category: 'file', keybind: new Keybind({key: 83, ctrl: true}), click: function () {saveFile();saveTextures();} }) new Action({ id: 'settings_window', icon: 'settings', category: 'blockbench', keybind: new Keybind({key: 69, ctrl: true}), click: function () {openSettings()} }) new Action({ id: 'plugins_window', icon: 'extension', category: 'blockbench', click: function () {showDialog('plugins')} }) new Action({ id: 'update_window', icon: 'update', category: 'blockbench', condition: isApp, click: function () {checkForUpdates()} }) new Action({ id: 'donate', icon: 'loyalty', category: 'blockbench', click: function () {Blockbench.openLink('http://blockbench.net/donate')} }) //Dialogs, UI new Action({ id: 'reset_keybindings', icon: 'replay', category: 'blockbench', click: function () {Keybinds.reset()} }) new Action({ id: 'import_layout', icon: 'folder', category: 'blockbench', click: function () { Blockbench.import({ extensions: ['bbstyle', 'js'], type: 'Blockbench Style' }, function(files) { applyBBStyle(files[0].content) }) } }) new Action({ id: 'export_layout', icon: 'style', category: 'blockbench', click: function () { Blockbench.export({ type: 'Blockbench Style', extensions: ['bbstyle'], content: autoStringify(app_colors) }) } }) new Action({ id: 'reset_layout', icon: 'replay', category: 'blockbench', click: function () { colorSettingsSetup(true) Interface.data = $.extend(true, {}, Interface.default_data) Interface.data.left_bar.forEach((id) => { $('#left_bar').append(Interface.Panels[id].node) }) Interface.data.right_bar.forEach((id) => { $('#right_bar').append(Interface.Panels[id].node) }) updateInterface() } }) new Action({ id: 'load_plugin', icon: 'fa-file-code-o', category: 'blockbench', click: function () { Blockbench.import({ extensions: ['bbplugin', 'js'], type: 'Blockbench Plugin', startpath: localStorage.getItem('plugin_dev_path') }, function(files) { loadPluginFromFile(files[0]) }) } }) //Edit new Action({ id: 'undo', icon: 'undo', category: 'edit', keybind: new Keybind({key: 90, ctrl: true}), click: function () {Undo.undo()} }) new Action({ id: 'redo', icon: 'redo', category: 'edit', keybind: new Keybind({key: 89, ctrl: true}), click: function () {Undo.redo()} }) new Action({ id: 'copy', icon: 'fa-clone', category: 'edit', work_in_dialog: true, keybind: new Keybind({key: 67, ctrl: true, shift: null}), click: function (event) {Clipbench.copy(event)} }) new Action({ id: 'paste', icon: 'fa-clipboard', category: 'edit', work_in_dialog: true, keybind: new Keybind({key: 86, ctrl: true, shift: null}), click: function (event) {Clipbench.paste(event)} }) new Action({ id: 'cut', icon: 'fa-scissors', category: 'edit', work_in_dialog: true, keybind: new Keybind({key: 88, ctrl: true, shift: null}), click: function (event) {Clipbench.copy(event, true)} }) new Action({ id: 'duplicate', icon: 'content_copy', category: 'edit', keybind: new Keybind({key: 68, ctrl: true}), click: function () { duplicateCubes(); } }) new Action({ id: 'delete', icon: 'delete', category: 'edit', keybind: new Keybind({key: 46}), click: function () { deleteCubes(); } }) new Action({ id: 'sort_outliner', icon: 'sort_by_alpha', category: 'edit', click: function () { Undo.initEdit({outliner: true}); sortOutliner(); Undo.finishEdit('sort_outliner') } }) new Action({ id: 'local_move', icon: 'check_box', category: 'edit', linked_setting: 'local_move', click: function () { BarItems.local_move.toggleLinkedSetting() updateSelection() } }) new Action({ id: 'select_window', icon: 'filter_list', category: 'edit', keybind: new Keybind({key: 70, ctrl: true}), click: function () { showDialog('selection_creator') $('#selgen_name').focus() } }) new Action({ id: 'invert_selection', icon: 'swap_vert', category: 'edit', keybind: new Keybind({key: 73, ctrl: true}), click: function () {invertSelection()} }) new Action({ id: 'select_all', icon: 'select_all', category: 'edit', keybind: new Keybind({key: 65, ctrl: true}), click: function () {selectAll()} }) new Action({ id: 'collapse_groups', icon: 'format_indent_decrease', category: 'edit', condition: function() { return TreeElements.length > 0 }, click: function () {collapseAllGroups()} }) //Transform new Action({ id: 'scale', icon: 'settings_overscan', category: 'transform', click: function () { $('#model_scale_range').val(1) $('#model_scale_label').val(1) Undo.initEdit({cubes: selected}) selected.forEach(function(obj) { obj.before = { from: obj.from.slice(), to: obj.to.slice(), origin: obj.origin.slice() } }) showDialog('scaling') } }) new Action({ id: 'rotate_x_cw', icon: 'rotate_right', color: 'x', category: 'transform', click: function () { rotateSelected(0, 1); } }) new Action({ id: 'rotate_x_ccw', icon: 'rotate_left', color: 'x', category: 'transform', click: function () { rotateSelected(0, 3); } }) new Action({ id: 'rotate_y_cw', icon: 'rotate_right', color: 'y', category: 'transform', click: function () { rotateSelected(1, 1); } }) new Action({ id: 'rotate_y_ccw', icon: 'rotate_left', color: 'y', category: 'transform', click: function () { rotateSelected(1, 3); } }) new Action({ id: 'rotate_z_cw', icon: 'rotate_right', color: 'z', category: 'transform', click: function () { rotateSelected(2, 1); } }) new Action({ id: 'rotate_z_ccw', icon: 'rotate_left', color: 'z', category: 'transform', click: function () { rotateSelected(2, 3); } }) new Action({ id: 'flip_x', icon: 'icon-mirror_x', color: 'x', category: 'transform', click: function () { mirrorSelected(0); } }) new Action({ id: 'flip_y', icon: 'icon-mirror_y', color: 'y', category: 'transform', click: function () { mirrorSelected(1); } }) new Action({ id: 'flip_z', icon: 'icon-mirror_z', color: 'z', category: 'transform', click: function () { mirrorSelected(2); } }) new Action({ id: 'center_x', icon: 'vertical_align_center', color: 'x', category: 'transform', click: function () { Undo.initEdit({cubes: selected}); centerCubes(0); Undo.finishEdit('center') } }) new Action({ id: 'center_y', icon: 'vertical_align_center', color: 'y', category: 'transform', click: function () { Undo.initEdit({cubes: selected}); centerCubes(1); Undo.finishEdit('center') } }) new Action({ id: 'center_z', icon: 'vertical_align_center', color: 'z', category: 'transform', click: function () { Undo.initEdit({cubes: selected}); centerCubes(2); Undo.finishEdit('center') } }) new Action({ id: 'center_all', icon: 'filter_center_focus', category: 'transform', click: function () { Undo.initEdit({cubes: selected}); centerCubesAll(); Undo.finishEdit('center') } }) new Action({ id: 'toggle_visibility', icon: 'visibility', category: 'transform', click: function () {toggleCubeProperty('visibility')} }) new Action({ id: 'toggle_export', icon: 'save', category: 'transform', click: function () {toggleCubeProperty('export')} }) new Action({ id: 'toggle_autouv', icon: 'fullscreen_exit', category: 'transform', click: function () {toggleCubeProperty('autouv')} }) new Action({ id: 'toggle_shade', icon: 'wb_sunny', category: 'transform', click: function () {toggleCubeProperty('shade')} }) new Action({ id: 'rename', icon: 'text_format', category: 'transform', keybind: new Keybind({key: 113}), click: function () {renameCubes()} }) new Action({ id: 'update_autouv', icon: 'brightness_auto', category: 'transform', condition: () => !Blockbench.entity_mode, click: function () { if (selected.length) { Undo.initEdit({cubes: selected[0].forSelected(), selection: true}) selected[0].forSelected(function(cube) { cube.mapAutoUV() }) Undo.finishEdit('update_autouv') } } }) //View new Action({ id: 'fullscreen', icon: 'fullscreen', category: 'view', condition: isApp, keybind: new Keybind({key: 122}), click: function () { currentwindow.setFullScreen(!currentwindow.isFullScreen()) } }) new Action({ id: 'zoom_in', icon: 'zoom_in', category: 'view', condition: isApp, click: function () {setZoomLevel('in')} }) new Action({ id: 'zoom_out', icon: 'zoom_out', category: 'view', condition: isApp, click: function () {setZoomLevel('out')} }) new Action({ id: 'zoom_reset', icon: 'zoom_out_map', category: 'view', condition: isApp, click: function () {setZoomLevel('reset')} }) new Action({ id: 'toggle_wireframe', icon: 'border_clear', category: 'view', keybind: new Keybind({key: 90}), condition: () => Toolbox && Toolbox.selected && Toolbox.selected.allowWireframe, click: function () {toggleWireframe()} }) new Action({ id: 'screenshot_model', icon: 'fa-cubes', category: 'view', keybind: new Keybind({key: 80, ctrl: true}), click: function () {quad_previews.current.screenshot()} }) new Action({ id: 'screenshot_app', icon: 'icon-bb_interface', category: 'view', click: function () {Screencam.fullScreen()} }) new Action({ id: 'toggle_quad_view', icon: 'widgets', category: 'view', keybind: new Keybind({key: 9}), click: function () { main_preview.toggleFullscreen() } }) //Textures new Action({ id: 'import_texture', icon: 'library_add', category: 'textures', keybind: new Keybind({key: 84, ctrl: true}), click: function () { openTexture() } }) new Action({ id: 'create_texture', icon: 'icon-create_bitmap', category: 'textures', keybind: new Keybind({key: 84, ctrl: true, shift: true}), click: function () { Painter.addBitmapDialog() } }) new Action({ id: 'reload_textures', icon: 'refresh', category: 'textures', keybind: new Keybind({key: 82, ctrl: true}), condition: isApp, click: reloadTextures }) new Action({ id: 'save_textures', icon: 'save', category: 'textures', keybind: new Keybind({key: 83, ctrl: true, alt: true}), click: function () {saveTextures()} }) new Action({ id: 'change_textures_folder', icon: 'fa-hdd-o', category: 'textures', condition: () => textures.length > 0, click: function () {changeTexturesFolder()} }) new Action({ id: 'animated_textures', icon: 'play_arrow', category: 'textures', condition: function() { if (Blockbench.entity_mode) return false; var i = 0; var show = false; while (i < textures.length) { if (textures[i].frameCount > 1) { show = true; i = textures.length } i++; } return show; }, click: function () { TextureAnimator.toggle() } }) //UV new Action({ id: 'uv_dialog', icon: 'view_module', category: 'blockbench', condition: ()=>!Blockbench.entity_mode && selected.length, click: function () {uv_dialog.openAll()} }) new Action({ id: 'uv_dialog_full', icon: 'web_asset', category: 'blockbench', click: function () {uv_dialog.openFull()} }) new BarSlider({ id: 'uv_rotation', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, min: 0, max: 270, step: 90, width: 80, onChange: function(slider) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('rotate') Undo.finishEdit('uv') } }) new BarSelect({ id: 'uv_grid', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, width: 60, options: { auto: true, '16': '16x16', '32': '32x32', '64': '64x64', none: true, }, onChange: function(slider) { if (open_dialog) { uv_dialog.changeGrid(slider.get()) } else { main_uv.setGrid() } } }) new Action({ id: 'uv_maximize', icon: 'zoom_out_map', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('maximize') Undo.finishEdit('uv') } }) new Action({ id: 'uv_auto', icon: 'brightness_auto', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('setAutoSize') Undo.finishEdit('uv') } }) new Action({ id: 'uv_rel_auto', icon: 'brightness_auto', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('setRelativeAutoSize') Undo.finishEdit('uv') } }) new Action({ id: 'uv_mirror_x', icon: 'icon-mirror_x', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('mirrorX') Undo.finishEdit('uv') } }) new Action({ id: 'uv_mirror_y', icon: 'icon-mirror_y', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('mirrorY') Undo.finishEdit('uv') } }) new Action({ id: 'uv_transparent', icon: 'clear', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('clear') Undo.finishEdit('uv') } }) new Action({ id: 'uv_reset', icon: 'replay', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('reset') Undo.finishEdit('uv') } }) new Action({ id: 'uv_apply_all', icon: 'format_color_fill', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function (e) { Undo.initEdit({cubes: selected, uv_only: true}) main_uv.applyAll(e) Undo.finishEdit('uv') } }) new BarSelect({ id: 'cullface', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, options: { off: tl('uv_editor.no_faces'), north: tl('face.north'), south: tl('face.south'), west: tl('face.west'), east: tl('face.east'), top: tl('face.up'), bottom: tl('face.down'), }, onChange: function(sel, event) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('switchCullface') Undo.finishEdit('uv') } }) new Action({ id: 'auto_cullface', icon: 'block', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function () { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('autoCullface') Undo.finishEdit('uv') } }) new Action({ id: 'face_tint', category: 'uv', condition: () => !Blockbench.entity_mode && selected.length, click: function (e) { Undo.initEdit({cubes: selected, uv_only: true}) uv_dialog.forSelection('switchTint', e) Undo.finishEdit('uv') } }) new Action({ id: 'uv_shift', condition: () => Blockbench.entity_mode, icon: 'photo_size_select_large', category: 'uv', click: function () { showUVShiftDialog() } }) //Rotations new Action({ id: 'origin_to_geometry', icon: 'filter_center_focus', category: 'transform', click: function () {origin2geometry()} }) new Action({ id: 'rescale_toggle', icon: 'check_box_outline_blank', category: 'transform', condition: function() {return !Blockbench.entity_mode && selected.length;}, click: function () { var value = !selected[0].rescale selected.forEach(function(cube) { cube.rescale = value }) updateNslideValues() } }) new Action({ id: 'bone_reset_toggle', icon: 'check_box_outline_blank', category: 'transform', condition: function() {return Blockbench.entity_mode && selected_group;}, click: function () { selected_group.reset = !selected_group.reset updateNslideValues() } }) //Outliner new Action({ id: 'add_cube', icon: 'add_box', category: 'edit', keybind: new Keybind({key: 78, ctrl: true}), condition: function() {return !Blockbench.entity_mode || selected_group || selected.length}, click: function () { addCube(); } }) new Action({ id: 'add_group', icon: 'create_new_folder', category: 'edit', keybind: new Keybind({key: 71, ctrl: true}), click: function () { addGroup(); } }) new Action({ id: 'outliner_toggle', icon: 'view_stream', category: 'edit', keybind: new Keybind({key: 115}), click: function () { toggleOutlinerOptions() } }) new BarText({ id: 'cube_counter', right: true, click: function() {selectAll()} }) //Display Mode new Action({ id: 'add_display_preset', icon: 'add', category: 'display', click: function () {showDialog('create_preset')} }) //Misc new Action({ id: 'reload', icon: 'refresh', category: 'file', condition: () => Blockbench.hasFlag('dev'), click: function () {Blockbench.reload()} }) }, setupToolbars: function() { // Toolbars = {} var stored = localStorage.getItem('toolbars') if (stored) { stored = JSON.parse(stored) if (typeof stored === 'object') { BARS.stored = stored } } Toolbars.outliner = new Toolbar({ id: 'outliner', children: [ 'add_cube', 'add_group', 'outliner_toggle', 'cube_counter' ], default_place: true }) Toolbars.texturelist = new Toolbar({ id: 'texturelist', children: [ 'import_texture', 'create_texture', 'reload_textures', 'animated_textures' ], default_place: true }) Toolbars.tools = new Toolbar({ id: 'tools', children: [ 'move_tool', 'resize_tool', 'vertex_snap_tool', 'brush_tool', 'display_mode_tool' ], default_place: true }) Toolbars.rotation = new Toolbar({ id: 'rotation', children: [ 'slider_rotation_x', 'slider_rotation_y', 'slider_rotation_z', 'rescale_toggle', 'bone_reset_toggle' ], default_place: true }) Toolbars.origin = new Toolbar({ id: 'origin', children: [ 'slider_origin_x', 'slider_origin_y', 'slider_origin_z', 'origin_to_geometry' ], default_place: true }) //UV Toolbars.main_uv = new Toolbar({ id: 'main_uv', children: [ 'uv_grid', 'uv_apply_all', 'uv_maximize', 'uv_auto', 'uv_transparent', 'uv_rotation', ], default_place: true }) Toolbars.uv_dialog = new Toolbar({ id: 'uv_dialog', children: [ 'uv_grid', '_', 'uv_select_all', 'uv_select_none', '_', 'uv_maximize', 'uv_auto', 'uv_rel_auto', '_', 'uv_mirror_x', 'uv_mirror_y', '_', 'copy', 'paste', '_', 'uv_transparent', 'uv_reset', '_', 'face_tint', '_', 'cullface', 'auto_cullface', '_', 'uv_rotation' ], default_place: true }) //Tools Toolbars.transform = new Toolbar({ id: 'transform', children: [ 'slider_pos_x', 'slider_pos_y', 'slider_pos_z', '_', 'slider_size_x', 'slider_size_y', 'slider_size_z', '_', 'slider_inflate' ] }) Toolbars.brush = new Toolbar({ id: 'brush', children: [ 'brush_mode', 'brush_color', 'slider_brush_size', 'slider_brush_opacity', 'slider_brush_softness' ] }) Toolbars.vertex_snap = new Toolbar({ id: 'vertex_snap', children: [ 'vertex_snap_mode' ] }) Toolbox = Toolbars.tools; Toolbox.toggleTransforms = function() { if (Toolbox.selected.id === 'move_tool') { BarItems['resize_tool'].select() } else if (Toolbox.selected.id === 'resize_tool') { BarItems['move_tool'].select() } } BarItems.move_tool.select() BarItems.reset_keybindings.toElement('#keybinds_title_bar') BarItems.import_layout.toElement('#layout_title_bar') BarItems.export_layout.toElement('#layout_title_bar') BarItems.reset_layout.toElement('#layout_title_bar') BarItems.load_plugin.toElement('#plugins_header_bar') BarItems.uv_dialog.toElement('#uv_title_bar') BarItems.uv_dialog_full.toElement('#uv_title_bar') }, setupVue: function() { BARS.list = new Vue({ el: '#toolbar_edit', data: { showAll: true, items: BarItems, currentBar: [] }, computed: { searchedBarItems() { var name = $('#action_search_bar').val().toUpperCase() var list = [{ icon: 'bookmark', name: tl('data.seperator'), type: 'seperator' }] if (this.showAll == false) { return list } for (var key in BarItems) { var item = BarItems[key] if (name.length == 0 || item.name.toUpperCase().includes(name) || item.id.toUpperCase().includes(name) ) { if ( BARS.condition(item.condition) && !this.currentBar.includes(item) ) { list.push(item) } } } return list; } }, methods: { sort: function(event) { var item = this.currentBar.splice(event.oldIndex, 1)[0] this.currentBar.splice(event.newIndex, 0, item) this.update() }, drop: function(event) { var scope = this; var index = event.oldIndex $('#bar_items_current .tooltip').css('display', '') setTimeout(() => { if ($('#bar_items_current:hover').length === 0) { var item = this.currentBar.splice(event.oldIndex, 1)[0] scope.update() } }, 30) }, choose: function(event) { $('#bar_items_current .tooltip').css('display', 'none') }, update: function() { BARS.editing_bar.update() }, addItem: function(item) { if (item.type === 'seperator') { item = '_' } this.currentBar.push(item) this.update() } } }) BARS.list.updateSearch = function() { BARS.list._data.showAll = !BARS.list._data.showAll BARS.list._data.showAll = !BARS.list._data.showAll } }, updateConditions: function() { for (var key in Toolbars) { if (Toolbars.hasOwnProperty(key) && $(Toolbars[key].node).find('input[type="text"]:focus, input[type="number"]:focus, div[contenteditable="true"]:focus').length === 0 ) { Toolbars[key].update() } } uv_dialog.all_editors.forEach((editor) => { editor.updateInterface() }) } } //Menu class Menu { constructor(structure) { var scope = this; this.children = []; this.node = $('')[0] this.structure = structure } hover(node, event) { if (event) event.stopPropagation() $(this.node).find('li.focused').removeClass('focused') $(this.node).find('li.opened').removeClass('opened') var obj = $(node) obj.addClass('focused') obj.parents('li.parent').addClass('opened') if (obj.hasClass('parent')) { var childlist = obj.find('> ul.contextMenu.sub') var p_width = obj.outerWidth() childlist.css('left', p_width + 'px') var el_width = childlist.width() var offset = childlist.offset() var el_height = childlist.height() if (offset.left + el_width > $(window).width()) { childlist.css('left', -el_width + 'px') } if (offset.top + el_height > $(window).height()) { childlist.css('margin-top', 4-childlist.height() + 'px') if (childlist.offset().top < 0) { var space = $(window).height() - $(window).height() childlist.offset({top: space/2}) } } if (el_height > $(window).height()) { childlist.css('height', $(window).height()+'px').css('overflow-y', 'scroll') } } } open(position, context) { var scope = this; var ctxmenu = $(this.node) if (open_menu) { open_menu.hide() } $('body').append(ctxmenu) ctxmenu.children().detach() function getEntry(s, parent) { var entry; if (s === '_') { entry = new MenuSeperator().menu_node var last = parent.children().last() if (last.length && !last.hasClass('menu_seperator')) { parent.append(entry) } } else if (typeof s === 'string' || s instanceof Action) { if (typeof s === 'string') { s = BarItems[s] } if (!s) { return; } s.menu_node.addEventListener('click', s.click) entry = s.menu_node if (BARS.condition(s.condition)) { parent.append(entry) $(entry).on('mouseenter mousedown', function(e) { scope.hover(this, e) }) } } else if (typeof s === 'object') { if (BARS.condition(s.condition, context)) { var icon = Blockbench.getIconNode(s.icon, s.color) entry = $('
  • ' + tl(s.name) + '
  • ') entry.prepend(icon) if (typeof s.click === 'function') { entry.click(function() {s.click(context)}) } //Submenu if (typeof s.children == 'function' || typeof s.children == 'object') { if (typeof s.children == 'function') { var list = s.children(context) } else { var list = s.children } if (list.length) { entry.addClass('parent') var childlist = $('') entry.append(childlist) list.forEach(function(s2, i) { getEntry(s2, childlist) }) var last = childlist.children().last() if (last.length && last.hasClass('menu_seperator')) { last.remove() } } } parent.append(entry) entry.mouseenter(function(e) { scope.hover(this, e) }) } } } this.structure.forEach(function(s, i) { getEntry(s, ctxmenu) }) var last = ctxmenu.children().last() if (last.length && last.hasClass('menu_seperator')) { last.remove() } var el_width = ctxmenu.width() var el_height = ctxmenu.height() if (position && position.clientX !== undefined) { var offset_left = position.clientX var offset_top = position.clientY } else { if (!position && scope.type === 'bar_menu') { position = scope.label } var offset_left = $(position).offset().left var offset_top = $(position).offset().top + $(position).height()+3 } if (offset_left > $(window).width() - el_width) { offset_left -= el_width } if (offset_top > $(window).height() - el_height ) { offset_top -= el_height } ctxmenu.css('left', offset_left+'px') ctxmenu.css('top', offset_top +'px') $(this.node).filter(':not(.tx)').addClass('tx').click(function(ev) { if ( ev.target.className.includes('parent') || (ev.target.parentNode && ev.target.parentNode.className.includes('parent')) ) {} else { scope.hide() } }) if (this.type === 'bar_menu') { MenuBar.open = this $(this.label).addClass('opened') } open_menu = this; return this; } show(position) { return this.open(position); } hide() { $(this.node).detach() open_menu = undefined; return this; } conditionMet() { if (this.condition === undefined) { return true; } else if (typeof this.condition === 'function') { return this.condition() } else { return !!this.condition } } addAction(action, path) { if (path === undefined) path = '' path = path.split('.') function traverse(arr, layer) { if (path.length === layer || path[layer] === '' || !isNaN(parseInt(path[layer]))) { var index = arr.length; if (path[layer] !== '' && path.length !== layer) { index = parseInt(path[layer]) } arr.splice(index, 0, action) } else { for (var i = 0; i < arr.length; i++) { var item = arr[i] if (item.children && item.children.length > 0 && item.id === path[layer] && layer < 20) { traverse(item.children, layer+1) i = 1000 } } } } traverse(this.structure, 0) } removeAction(path) { if (path === undefined) path = '' path = path.split('.') function traverse(arr, layer) { var result; if (!isNaN(parseInt(path[layer]))) { result = arr[parseInt(path[layer])] } else if (typeof path[layer] === 'string') { var i = arr.length-1; while (i >= 0) { var item = arr[i] if (item.id === path[layer] && layer < 20) { if (layer === path.length-1) { arr.splice(i, 1) } else if (item.children) { traverse(item.children, layer+1) } } i--; } } } traverse(this.structure, 0) } } class BarMenu extends Menu { constructor(id, structure, condition) { super() var scope = this; this.type = 'bar_menu' this.id = id this.children = []; this.condition = condition this.node = $('')[0] this.label = $('')[0] $(this.label).click(function() { scope.show() }) $(this.label).mouseenter(function() { if (MenuBar.open && MenuBar.open !== scope) { scope.show() } }) this.structure = structure } hide() { $(this.node).detach() $(this.label).removeClass('opened') MenuBar.open = undefined open_menu = undefined; return this; } } var MenuBar = { menues: {}, open: undefined, setup: function() { MenuBar.menues.file = new BarMenu('file', [ 'project_window', {name: 'menu.file.new', id: 'new', icon: 'insert_drive_file', children: [ 'new_block_model', 'new_entity_model' ]}, {name: 'menu.file.recent', id: 'recent', icon: 'history', condition: function() {return isApp && recent_projects.length}, children: function() { var arr = [] recent_projects.forEach(function(p) { var entity = p.name.substr(0,4) === 'mobs' arr.splice(0, 0, { name: p.name, path: p.path, icon: entity ? 'view_list' : 'insert_drive_file', click: function() { readFile(p.path, true) } }) }) return arr }}, 'open_model', {name: 'menu.file.import', id: 'import', icon: 'insert_drive_file', children: [ 'add_model', 'extrude_texture' ]}, {name: 'menu.file.export', id: 'export', icon: 'insert_drive_file', children: [ 'export_blockmodel', 'export_entity', 'export_optifine_part', 'export_optifine_full', 'export_obj' ]}, 'save', '_', 'settings_window', 'update_window', 'show_tip', 'donate', 'reload' ]) MenuBar.menues.edit = new BarMenu('edit', [ 'undo', 'redo', '_', 'add_cube', 'duplicate', 'delete', 'sort_outliner', '_', 'local_move', '_', 'select_window', 'invert_selection' ], function() {return !display_mode}) MenuBar.menues.transform = new BarMenu('transform', [ 'scale', {name: 'menu.transform.rotate', id: 'rotate', icon: 'rotate_90_degrees_ccw', children: [ 'rotate_x_cw', 'rotate_x_ccw', 'rotate_y_cw', 'rotate_y_ccw', 'rotate_z_cw', 'rotate_z_ccw' ]}, {name: 'menu.transform.flip', id: 'flip', icon: 'flip', children: [ 'flip_x', 'flip_y', 'flip_z' ]}, {name: 'menu.transform.center', id: 'center', icon: 'filter_center_focus', children: [ 'center_x', 'center_y', 'center_z', 'center_all' ]}, {name: 'menu.transform.properties', id: 'properties', icon: 'navigate_next', children: [ 'toggle_visibility', 'toggle_export', 'toggle_autouv', 'toggle_shade', 'rename' ]} ], function() {return !display_mode}) MenuBar.menues.filter = new BarMenu('filter', [ 'plugins_window', '_' /* plaster optimize sort by transparency entity / player model / shape generator */ ], function() {return !display_mode}) MenuBar.menues.display = new BarMenu('display', [ 'copy', 'paste', '_', 'add_display_preset', {name: 'menu.display.preset', icon: 'fa-list', children: function() { var presets = [] display_presets.forEach(function(p) { var icon = 'label' if (p.fixed) { switch(p.id) { case 'item': icon = 'filter_vintage'; break; case 'block': icon = 'fa-cube'; break; case 'handheld': icon = 'build'; break; case 'rod': icon = 'remove'; break; } } presets.push({ icon: icon, name: p.id ? tl('display.preset.'+p.id) : p.name, click: function() { applyDisplayPreset(p) } }) }) return presets; }}, {name: 'menu.display.preset_all', icon: 'fa-list', children: function() { var presets = [] display_presets.forEach(function(p) { var icon = 'label' if (p.fixed) { switch(p.id) { case 'item': icon = 'filter_vintage'; break; case 'block': icon = 'fa-cube'; break; case 'handheld': icon = 'build'; break; case 'rod': icon = 'remove'; break; } } presets.push({ icon: icon, name: p.id ? tl('display.preset.'+p.id) : p.name, click: function() { applyDisplayPreset(p, true) } }) }) return presets; }}, {name: 'menu.display.remove_preset', icon: 'fa-list', children: function() { var presets = [] display_presets.forEach(function(p) { if (!p.fixed) { presets.push({ icon: 'label', name: p.name, click: function() { display_presets.splice(display_presets.indexOf(p),1) } }) } }) return presets; }} ], function() {return display_mode}) MenuBar.menues.view = new BarMenu('view', [ 'fullscreen', {name: 'menu.view.zoom', id: 'zoom', condition: isApp, icon: 'search', children: [ 'zoom_in', 'zoom_out' ]}, '_', 'toggle_wireframe', 'toggle_quad_view', {name: 'menu.view.screenshot', id: 'screenshot', condition: isApp, icon: 'camera_alt', children: [ 'screenshot_model', 'screenshot_app' ]}, ]) MenuBar.update() }, update: function() { var bar = $('#menu_bar') bar.children().detach() this.keys = [] for (var menu in MenuBar.menues) { if (MenuBar.menues.hasOwnProperty(menu)) { if (MenuBar.menues[menu].conditionMet()) { bar.append(MenuBar.menues[menu].label) this.keys.push(menu) } } } }, getNode: function(data) { }, addAction: function(action, path) { if (path) { path = path.split('.') var menu = MenuBar.menues[path.splice(0, 1)[0]] if (menu) { menu.addAction(action, path.join('.')) } } }, removeAction: function(path) { if (path) { path = path.split('.') var menu = MenuBar.menues[path.splice(0, 1)[0]] if (menu) { menu.removeAction(path.join('.')) } } } } var Keybinds = { actions: [], stored: {}, extra: {}, structure: {}, save: function() { localStorage.setItem('keybindings', JSON.stringify(Keybinds.stored)) }, reset: function() { for (var category in Keybinds.structure) { var entries = Keybinds.structure[category].actions if (entries && entries.length) { entries.forEach(function(item) { if (item.keybind) { if (item.default_keybind) { item.keybind.set(item.default_keybind); } else { item.keybind.clear(); } item.keybind.save(false) } }) } } Keybinds.save() } } if (localStorage.getItem('keybindings')) { try { Keybinds.stored = JSON.parse(localStorage.getItem('keybindings')) } catch (err) {} } Toolbar.prototype.menu = new Menu([ //Needs to be down here because Menu isn't defined before {name: 'menu.toolbar.edit', icon: 'edit', click: function(bar) { bar.editMenu() }}, {name: 'menu.toolbar.reset', icon: 'refresh', click: function(bar) { bar.reset() }} ])