Refactor panels

This commit is contained in:
JannisX11 2020-09-21 23:23:42 +02:00
parent d34031eaaa
commit f130efd421
16 changed files with 581 additions and 532 deletions

View File

@ -1,6 +1,12 @@
.panel {
padding-bottom: 4px;
}
/*Panel*/
.panel {
padding-bottom: 4px;
}
.panel.grow > .panel_inside {
flex-grow: 1;
display: flex;
flex-direction: column;
}
/*Display*/

View File

@ -69,7 +69,6 @@
<script src="js/interface/dialog.js"></script>
<script src="js/copy_paste.js"></script>
<script src="js/undo.js"></script>
<script src="js/edit_sessions.js"></script>
<script type="text/javascript">
if (isApp === true) {
@ -81,6 +80,9 @@
<script src="js/api.js"></script>
<script src="js/file_system.js"></script>
<script src="js/interface/panels.js"></script>
<script src="js/interface/interface.js"></script>
<script src="js/edit_sessions.js"></script>
<script src="js/outliner/outliner.js"></script>
<script src="js/outliner/group.js"></script>
<script src="js/outliner/cube.js"></script>
@ -91,8 +93,6 @@
<script src="js/transform.js"></script>
<script src="js/texturing/textures.js"></script>
<script src="js/texturing/uv.js"></script>
<script src="js/interface/panels.js"></script>
<script src="js/interface/interface.js"></script>
<script src="js/texturing/painter.js"></script>
<script src="js/texturing/texture_generator.js"></script>
<script src="js/texturing/color.js"></script>
@ -735,117 +735,6 @@
</div>
</div>
<div id="display" class="panel">
<div class="toolbar_wrapper display"></div>
<p class="tl">display.slot</p>
<div id="display_bar" class="bar tabs_small icon_bar">
<input class="hidden" type="radio" name="display" id="thirdperson_righthand" checked>
<label class="tool" for="thirdperson_righthand" onclick="DisplayMode.loadThirdRight()"><div class="tooltip tl">display.slot.third_right</div><i class="material-icons">accessibility</i></label>
<input class="hidden" type="radio" name="display" id="thirdperson_lefthand">
<label class="tool" for="thirdperson_lefthand" onclick="DisplayMode.loadThirdLeft()"><div class="tooltip tl">display.slot.third_left</div><i class="material-icons">accessibility</i></label>
<input class="hidden" type="radio" name="display" id="firstperson_righthand">
<label class="tool" for="firstperson_righthand" onclick="DisplayMode.loadFirstRight()"><div class="tooltip tl">display.slot.first_right</div><i class="material-icons">person</i></label>
<input class="hidden" type="radio" name="display" id="firstperson_lefthand">
<label class="tool" for="firstperson_lefthand" onclick="DisplayMode.loadFirstLeft()"><div class="tooltip tl">display.slot.first_left</div><i class="material-icons">person</i></label>
<input class="hidden" type="radio" name="display" id="head">
<label class="tool" for="head" onclick="DisplayMode.loadHead()"><div class="tooltip tl">display.slot.head</div><i class="material-icons">sentiment_satisfied</i></label>
<input class="hidden" type="radio" name="display" id="ground">
<label class="tool" for="ground" onclick="DisplayMode.loadGround()"><div class="tooltip tl">display.slot.ground</div><i class="icon-ground"></i></label>
<input class="hidden" type="radio" name="display" id="fixed">
<label class="tool" for="fixed" onclick="DisplayMode.loadFixed()"><div class="tooltip tl">display.slot.frame</div><i class="material-icons">filter_frames</i></label>
<input class="hidden" type="radio" name="display" id="gui">
<label class="tool" for="gui" onclick="DisplayMode.loadGUI()"><div class="tooltip tl">display.slot.gui</div><i class="material-icons">border_style</i></label>
</div>
<p class="reference_model_bar tl">display.reference</p>
<div id="display_ref_bar" class="bar tabs_small reference_model_bar">
</div>
<div id="display_sliders">
<p class="tl">display.rotation</p><div class="tool head_right" v-on:click="resetChannel('rotation')"><i class="material-icons">replay</i></div>
<div class="bar slider_input_combo" v-for="axis in axes">
<input type="range" class="tool disp_range" v-model.number="slot.rotation[axis]" v-bind:trigger_type="'rotation.'+axis"
min="-180" max="180" step="1" value="0"
@input="change(axis, 'rotation')" @mousedown="start" @change="save">
<input lang="en" type="number" class="tool disp_text" v-model.number="slot.rotation[axis]" min="-180" max="180" step="0.5" value="0" @input="change(axis, 'rotation');save()" @mousedown="start">
<div class="color_corner" :style="{'border-color': `var(--color-axis-${getAxisLetter(axis)})`}"></div>
</div>
<p class="tl">display.translation</p><div class="tool head_right" v-on:click="resetChannel('translation')"><i class="material-icons">replay</i></div>
<div class="bar slider_input_combo" v-for="axis in axes">
<input type="range" class="tool disp_range" v-model.number="slot.translation[axis]" v-bind:trigger_type="'translation.'+axis"
v-bind:min="Math.abs(slot.translation[axis]) < 10 ? -20 : (slot.translation[axis] > 0 ? -70*3+10 : -80)"
v-bind:max="Math.abs(slot.translation[axis]) < 10 ? 20 : (slot.translation[axis] < 0 ? 70*3-10 : 80)"
v-bind:step="Math.abs(slot.translation[axis]) < 10 ? 0.25 : 1"
value="0" @input="change(axis, 'translation')" @mousedown="start" @change="save">
<input lang="en" type="number" class="tool disp_text" v-model.number="slot.translation[axis]" min="-80" max="80" step="0.5" value="0" @input="change(axis, 'translation');save()" @mousedown="start">
<div class="color_corner" :style="{'border-color': `var(--color-axis-${getAxisLetter(axis)})`}"></div>
</div>
<p class="tl">display.scale</p><div class="tool head_right" v-on:click="resetChannel('scale')"><i class="material-icons">replay</i></div>
<div class="bar slider_input_combo" v-for="axis in axes">
<div class="tool display_scale_invert" v-on:click="invert(axis)">
<div class="tooltip tl">display.mirror</div>
<i class="material-icons">{{ slot.mirror[axis] ? 'check_box' : 'check_box_outline_blank' }}</i>
</div>
<input type="range" class="tool disp_range scaleRange" v-model.number="slot.scale[axis]" v-bind:trigger_type="'scale.'+axis" v-bind:id="'scale_range_'+axis"
v-bind:min="slot.scale[axis] > 1 ? -2 : 0"
v-bind:max="slot.scale[axis] > 1 ? 4 : 2"
step="0.01"
value="0" @input="change(axis, 'scale')" @mousedown="start" @change="save">
<input type="number" class="tool disp_text" v-model.number="slot.scale[axis]" min="0" max="4" step="0.01" value="0" @input="change(axis, 'scale');save()" @mousedown="start">
<div class="color_corner" :style="{'border-color': `var(--color-axis-${getAxisLetter(axis)})`}"></div>
</div>
</div>
</div>
<div id="textures" class="panel grow">
<div class="toolbar_wrapper texturelist"></div>
<ul id="texture_list" class="list">
<li
v-for="texture in textures"
v-bind:class="{ selected: texture.selected, particle: texture.particle}"
v-bind:texid="texture.uuid"
:key="texture.uuid"
class="texture"
v-on:click.stop="texture.select($event)"
v-on:dblclick="texture.openMenu($event)"
@contextmenu.prevent.stop="texture.showContextMenu($event)"
>
<div class="texture_icon_wrapper">
<img v-bind:texid="texture.id" v-bind:src="texture.source" class="texture_icon" width="48px" alt="" v-if="texture.show_icon" />
<i class="material-icons texture_error" v-bind:title="texture.getErrorMessage()" v-if="texture.error">error_outline</i>
<i class="texture_movie fa fa_big fa-film" title="Animated Texture" v-if="texture.frameCount > 1"></i>
</div>
<div class="texture_description_wrapper">
<div class="texture_name">{{ texture.name }}</div>
<div class="texture_res">{{ texture.error
? texture.getErrorMessage()
: texture.width + ' x ' + texture.height + 'px'
}}</div>
</div>
<i class="material-icons texture_visibility_icon" v-if="texture.particle">bubble_chart</i>
<i class="material-icons texture_particle_icon clickable"
v-bind:class="{icon_off: !texture.visible}"
v-if="Project.layered_textures"
@click="texture.toggleVisibility()"
@dblclick.stop
>
{{ texture.visible ? 'visibility' : 'visibility_off' }}
</i>
<i class="material-icons texture_save_icon" v-bind:class="{clickable: !texture.saved}" @click="texture.save()">
<template v-if="texture.saved">check_circle</template>
<template v-else>save</template>
</i>
</li>
</ul>
</div>
</div>
<div id="right_bar" class="sidebar">
@ -911,12 +800,6 @@
</div>
</div>
<div id="outliner" class="panel grow">
<div class="toolbar_wrapper outliner"></div>
<ul id="cubes_list" class="list">
<vue-tree :option="option"></vue-tree>
</ul>
</div>
</div>
<div id="center">
<div id="preview">

View File

@ -1460,3 +1460,133 @@ BARS.defineActions(function() {
})*/
})
Interface.definePanels(function() {
Interface.Panels.animations = new Panel({
id: 'animations',
icon: 'movie',
condition: {modes: ['animate']},
toolbars: {
head: Toolbars.animations
},
component: {
name: 'panel-animations',
data() { return {
animations: Animator.animations,
files_folded: {}
}},
methods: {
toggle(key) {
this.files_folded[key] = !this.files_folded[key];
this.$forceUpdate();
},
saveFile(key, file) {
if (key && isApp) {
file.animations.forEach(animation => {
animation.save();
})
} else {
}
},
addAnimation(path) {
let other_animation = Animation.all.find(a => a.path == path)
console.log(path, other_animation)
new Animation({
name: other_animation && other_animation.name.replace(/\w+$/, 'new'),
path
}).add(true).propertiesDialog()
}
},
computed: {
files() {
let files = {};
this.animations.forEach(animation => {
let key = animation.path || '';
if (!files[key]) files[key] = {
animations: [],
name: animation.path ? pathToName(animation.path, true) : 'Unsaved',
saved: true
};
if (!animation.saved) files[key].saved = false;
files[key].animations.push(animation);
})
return files;
}
},
template: `
<div>
<div class="toolbar_wrapper animations"></div>
<ul id="animations_list" class="list">
<li v-for="(file, key) in files" :key="key" class="animation_file">
<div class="animation_file_head" v-on:click.stop="toggle(key)">
<i v-on:click.stop="toggle(key)" class="icon-open-state fa" :class=\'{"fa-angle-right": files_folded[key], "fa-angle-down": !files_folded[key]}\'></i>
<label>{{ file.name }}</label>
<div class="in_list_button" v-if="!file.saved" v-on:click.stop="saveFile(key, file)">
<i class="material-icons">save</i>
</div>
<div class="in_list_button" v-on:click.stop="addAnimation(key)">
<i class="material-icons">add</i>
</div>
</div>
<ul v-if="!files_folded[key]">
<li
v-for="animation in file.animations"
v-bind:class="{ selected: animation.selected }"
v-bind:anim_id="animation.uuid"
class="animation"
v-on:click.stop="animation.select()"
v-on:dblclick.stop="animation.propertiesDialog()"
:key="animation.uuid"
@contextmenu.prevent.stop="animation.showContextMenu($event)"
>
<i class="material-icons">movie</i>
<label>{{ animation.name }}</label>
<div class="in_list_button" v-bind:class="{unclickable: animation.saved}" v-on:click.stop="animation.save()">
<i v-if="animation.saved" class="material-icons">check_circle</i>
<i v-else class="material-icons">save</i>
</div>
<div class="in_list_button" v-on:click.stop="animation.togglePlayingState()">
<i v-if="animation.playing" class="fa_big far fa-play-circle"></i>
<i v-else class="fa_big far fa-circle"></i>
</div>
</li>
</ul>
</li>
</ul>
</div>
`
}
})
Interface.Panels.variable_placeholders = new Panel({
id: 'variable_placeholders',
icon: 'fas.fa-stream',
condition: {modes: ['animate']},
growable: true,
toolbars: {
},
component: {
name: 'panel-placeholders',
components: {VuePrismEditor},
data() { return {
text: ''
}},
template: `
<div style="flex-grow: 1; display: flex; flex-direction: column;">
<p>{{ tl('panel.variable_placeholders.info') }}</p>
<vue-prism-editor
id="var_placeholder_area"
class="molang_input dark_bordered tab_target"
v-model="text"
language="molang"
:line-numbers="false"
style="flex-grow: 1;"
onkeyup="Animator.preview()"
/>
</div>
`
}
})
})

View File

@ -641,3 +641,90 @@ BARS.defineActions(function() {
}
})
})
Interface.definePanels(function() {
Interface.Panels.keyframe = new Panel({
id: 'keyframe',
icon: 'timeline',
condition: {modes: ['animate']},
toolbars: {
head: Toolbars.keyframe
},
component: {
name: 'panel-keyframe',
components: {VuePrismEditor},
data() { return {
keyframes: Timeline.selected
}},
methods: {
updateInput(axis, value) {
console.log(this.keyframes[0].x)
updateKeyframeValue(axis, value)
},
getKeyframeInfos() {
let list = [tl('timeline.'+this.channel)];
if (this.keyframes.length > 1) list.push(this.keyframes.length);
/*if (this.keyframes[0].color >= 0) {
list.push(tl(`cube.color.${markerColors[this.keyframes[0].color].name}`))
}*/
return list.join(', ')
}
},
computed: {
channel() {
var channel = false;
for (var kf of this.keyframes) {
if (channel === false) {
channel = kf.channel
} else if (channel !== kf.channel) {
channel = false
break;
}
}
return channel;
}
},
template: `
<div>
<div class="toolbar_wrapper keyframe"></div>
<template v-if="channel != false">
<p id="keyframe_type_label">{{ tl('panel.keyframe.type', [getKeyframeInfos()]) }}</p>
<div class="bar flex" id="keyframe_bar_x" v-if="keyframes[0].animator instanceof BoneAnimator">
<label class="color_x" style="font-weight: bolder">X</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" :value="keyframes[0].x.toString()" v-model="keyframes[0].x" @change="updateInput('x', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar flex" id="keyframe_bar_y" v-if="keyframes[0].animator instanceof BoneAnimator">
<label class="color_y" style="font-weight: bolder">Y</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" :value="keyframes[0].y.toString()" v-model="keyframes[0].y" @change="updateInput('y', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar flex" id="keyframe_bar_z" v-if="keyframes[0].animator instanceof BoneAnimator">
<label class="color_z" style="font-weight: bolder">Z</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" :value="keyframes[0].z.toString()" v-model="keyframes[0].z" @change="updateInput('z', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar flex" id="keyframe_bar_effect" v-if="channel == 'particle' || channel == 'sound'">
<label>{{ tl('data.effect') }}</label>
<input type="text" class="dark_bordered code keyframe_input tab_target" v-model="keyframes[0].effect" @input="updateInput('effect', $event)">
</div>
<div class="bar flex" id="keyframe_bar_locator" v-if="channel == 'particle'">
<label>{{ tl('data.locator') }}</label>
<input @focus="focus()" @focusout="focusout()" type="text" class="dark_bordered code keyframe_input tab_target" v-model="keyframes[0].locator" @input="updateInput('locator', $event)">
</div>
<div class="bar flex" id="keyframe_bar_script" v-if="channel == 'particle'">
<label>{{ tl('timeline.pre_effect_script') }}</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" v-model="keyframes[0].script" @change="updateInput('script', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar" id="keyframe_bar_instructions" v-if="channel == 'timeline'">
<label>{{ tl('timeline.timeline') }}</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" v-model="keyframes[0].instructions" @change="updateInput('instructions', $event)" language="molang" :line-numbers="false" />
</div>
</template>
</div>
`
}
})
})

View File

@ -2,7 +2,6 @@ var osfs = '/'
var selected = [];
var prev_side = 'north';
var uv_clipboard;
var outliner, texturelist;
var pe_list_data = []
var open_dialog = false;
var open_interface = false;

View File

@ -91,25 +91,6 @@ onVueSetup.funcs.forEach((func) => {
}
})
$('#cubes_list').droppable({
greedy: true,
accept: 'div.outliner_object',
tolerance: 'pointer',
hoverClass: 'drag_hover',
drop: function(event, ui) {
var item = Outliner.root.findRecursive('uuid', $(ui.draggable).parent().attr('id'))
dropOutlinerObjects(item, undefined, event)
}
})
$('#cubes_list').contextmenu(function(event) {
event.stopPropagation();
event.preventDefault();
Interface.Panels.outliner.menu.show(event)
})
$('#texture_list').contextmenu(function(event) {
Interface.Panels.textures.menu.show(event)
})
updateProjectResolution()
setupInterface()

View File

@ -1864,60 +1864,6 @@ function updateDisplaySkin() {
//displayReferenceObjects.refmodels.player.material
}
onVueSetup(function() {
DisplayMode.vue = new Vue({
el: '#display_sliders',
data: {
axes: [0, 1, 2],
slot: new DisplaySlot()
},
methods: {
isMirrored: (axis) => {
if (display[display_slot]) {
return display[display_slot].scale[axis] < 0;
}
},
change: (axis, channel) => {
if (channel === 'scale') {
var val = limitNumber(DisplayMode.slot.scale[axis], 0, 4)
DisplayMode.slot.scale[axis] = val;
if (Pressing.shift) {
DisplayMode.slot.scale[0] = val;
DisplayMode.slot.scale[1] = val;
DisplayMode.slot.scale[2] = val;
}
} else if (channel === 'translation') {
DisplayMode.slot.translation[axis] = limitNumber(DisplayMode.slot.translation[axis], -80, 80)||0;
} else {
DisplayMode.slot.rotation[axis] = Math.trimDeg(DisplayMode.slot.rotation[axis])||0;
}
DisplayMode.updateDisplayBase()
},
resetChannel: (channel) => {
var v = channel === 'scale' ? 1 : 0;
Undo.initEdit({display_slots: [display_slot]})
DisplayMode.slot.extend({[channel]: [v, v, v]})
if (channel === 'scale') {
DisplayMode.slot.extend({mirror: [false, false, false]})
}
Undo.finishEdit('reset display')
},
invert: (axis) => {
Undo.initEdit({display_slots: [display_slot]})
DisplayMode.slot.mirror[axis] = !DisplayMode.slot.mirror[axis];
DisplayMode.slot.update()
Undo.finishEdit('mirror display')
},
start: () => {
Undo.initEdit({display_slots: [display_slot]})
},
save: () => {
Undo.finishEdit('change_display')
}
}
})
})
BARS.defineActions(function() {
new Action('add_display_preset', {
icon: 'add',
@ -1983,4 +1929,139 @@ BARS.defineActions(function() {
})
})
Interface.definePanels(function() {
Interface.Panels.display = new Panel({
id: 'display',
icon: 'tune',
condition: {modes: ['display']},
toolbars: {
head: Toolbars.display
},
component: {
name: 'panel-keyframe',
components: {VuePrismEditor},
data() {return {
axes: [0, 1, 2],
slot: new DisplaySlot()
}},
methods: {
isMirrored: (axis) => {
if (display[display_slot]) {
return display[display_slot].scale[axis] < 0;
}
},
change: (axis, channel) => {
if (channel === 'scale') {
var val = limitNumber(DisplayMode.slot.scale[axis], 0, 4)
DisplayMode.slot.scale[axis] = val;
if (Pressing.shift) {
DisplayMode.slot.scale[0] = val;
DisplayMode.slot.scale[1] = val;
DisplayMode.slot.scale[2] = val;
}
} else if (channel === 'translation') {
DisplayMode.slot.translation[axis] = limitNumber(DisplayMode.slot.translation[axis], -80, 80)||0;
} else {
DisplayMode.slot.rotation[axis] = Math.trimDeg(DisplayMode.slot.rotation[axis])||0;
}
DisplayMode.updateDisplayBase()
},
resetChannel: (channel) => {
var v = channel === 'scale' ? 1 : 0;
Undo.initEdit({display_slots: [display_slot]})
DisplayMode.slot.extend({[channel]: [v, v, v]})
if (channel === 'scale') {
DisplayMode.slot.extend({mirror: [false, false, false]})
}
Undo.finishEdit('reset display')
},
invert: (axis) => {
Undo.initEdit({display_slots: [display_slot]})
DisplayMode.slot.mirror[axis] = !DisplayMode.slot.mirror[axis];
DisplayMode.slot.update()
Undo.finishEdit('mirror display')
},
start: () => {
Undo.initEdit({display_slots: [display_slot]})
},
save: () => {
Undo.finishEdit('change_display')
}
},
template: `
<div>
<div class="toolbar_wrapper display"></div>
<p>${ tl('display.slot') }</p>
<div id="display_bar" class="bar tabs_small icon_bar">
<input class="hidden" type="radio" name="display" id="thirdperson_righthand" checked>
<label class="tool" for="thirdperson_righthand" onclick="DisplayMode.loadThirdRight()"><div class="tooltip">${ tl('display.slot.third_right') }</div><i class="material-icons">accessibility</i></label>
<input class="hidden" type="radio" name="display" id="thirdperson_lefthand">
<label class="tool" for="thirdperson_lefthand" onclick="DisplayMode.loadThirdLeft()"><div class="tooltip">${ tl('display.slot.third_left') }</div><i class="material-icons">accessibility</i></label>
<input class="hidden" type="radio" name="display" id="firstperson_righthand">
<label class="tool" for="firstperson_righthand" onclick="DisplayMode.loadFirstRight()"><div class="tooltip">${ tl('display.slot.first_right') }</div><i class="material-icons">person</i></label>
<input class="hidden" type="radio" name="display" id="firstperson_lefthand">
<label class="tool" for="firstperson_lefthand" onclick="DisplayMode.loadFirstLeft()"><div class="tooltip">${ tl('display.slot.first_left') }</div><i class="material-icons">person</i></label>
<input class="hidden" type="radio" name="display" id="head">
<label class="tool" for="head" onclick="DisplayMode.loadHead()"><div class="tooltip">${ tl('display.slot.head') }</div><i class="material-icons">sentiment_satisfied</i></label>
<input class="hidden" type="radio" name="display" id="ground">
<label class="tool" for="ground" onclick="DisplayMode.loadGround()"><div class="tooltip">${ tl('display.slot.ground') }</div><i class="icon-ground"></i></label>
<input class="hidden" type="radio" name="display" id="fixed">
<label class="tool" for="fixed" onclick="DisplayMode.loadFixed()"><div class="tooltip">${ tl('display.slot.frame') }</div><i class="material-icons">filter_frames</i></label>
<input class="hidden" type="radio" name="display" id="gui">
<label class="tool" for="gui" onclick="DisplayMode.loadGUI()"><div class="tooltip">${ tl('display.slot.gui') }</div><i class="material-icons">border_style</i></label>
</div>
<p class="reference_model_bar">${ tl('display.reference') }</p>
<div id="display_ref_bar" class="bar tabs_small reference_model_bar">
</div>
<div id="display_sliders">
<p>${ tl('display.rotation') }</p><div class="tool head_right" v-on:click="resetChannel('rotation')"><i class="material-icons">replay</i></div>
<div class="bar slider_input_combo" v-for="axis in axes">
<input type="range" class="tool disp_range" v-model.number="slot.rotation[axis]" v-bind:trigger_type="'rotation.'+axis"
min="-180" max="180" step="1" value="0"
@input="change(axis, 'rotation')" @mousedown="start" @change="save">
<input lang="en" type="number" class="tool disp_text" v-model.number="slot.rotation[axis]" min="-180" max="180" step="0.5" value="0" @input="change(axis, 'rotation');save()" @mousedown="start">
<div class="color_corner" :style="{'border-color': \`var(--color-axis-\${getAxisLetter(axis)})\`}"></div>
</div>
<p>${ tl('display.translation') }</p><div class="tool head_right" v-on:click="resetChannel('translation')"><i class="material-icons">replay</i></div>
<div class="bar slider_input_combo" v-for="axis in axes">
<input type="range" class="tool disp_range" v-model.number="slot.translation[axis]" v-bind:trigger_type="'translation.'+axis"
v-bind:min="Math.abs(slot.translation[axis]) < 10 ? -20 : (slot.translation[axis] > 0 ? -70*3+10 : -80)"
v-bind:max="Math.abs(slot.translation[axis]) < 10 ? 20 : (slot.translation[axis] < 0 ? 70*3-10 : 80)"
v-bind:step="Math.abs(slot.translation[axis]) < 10 ? 0.25 : 1"
value="0" @input="change(axis, 'translation')" @mousedown="start" @change="save">
<input lang="en" type="number" class="tool disp_text" v-model.number="slot.translation[axis]" min="-80" max="80" step="0.5" value="0" @input="change(axis, 'translation');save()" @mousedown="start">
<div class="color_corner" :style="{'border-color': \`var(--color-axis-\${getAxisLetter(axis)})\`}"></div>
</div>
<p>${ tl('display.scale') }</p><div class="tool head_right" v-on:click="resetChannel('scale')"><i class="material-icons">replay</i></div>
<div class="bar slider_input_combo" v-for="axis in axes">
<div class="tool display_scale_invert" v-on:click="invert(axis)">
<div class="tooltip">${ tl('display.mirror') }</div>
<i class="material-icons">{{ slot.mirror[axis] ? 'check_box' : 'check_box_outline_blank' }}</i>
</div>
<input type="range" class="tool disp_range scaleRange" v-model.number="slot.scale[axis]" v-bind:trigger_type="'scale.'+axis" v-bind:id="'scale_range_'+axis"
v-bind:min="slot.scale[axis] > 1 ? -2 : 0"
v-bind:max="slot.scale[axis] > 1 ? 4 : 2"
step="0.01"
value="0" @input="change(axis, 'scale')" @mousedown="start" @change="save">
<input type="number" class="tool disp_text" v-model.number="slot.scale[axis]" min="0" max="4" step="0.01" value="0" @input="change(axis, 'scale');save()" @mousedown="start">
<div class="color_corner" :style="{'border-color': \`var(--color-axis-\${getAxisLetter(axis)})\`}"></div>
</div>
</div>
</div>
`
},
})
DisplayMode.vue = Interface.Panels.display.inside_vue;
})
})()

View File

@ -427,4 +427,40 @@ BARS.defineActions(function() {
category: 'blockbench',
click: () => (Chat.toggle())
})
})
Interface.definePanels(function() {
Interface.Panels.chat = new Panel({
id: 'chat',
icon: 'chat',
condition: {method() {return EditSession.active}},
toolbars: {},
onResize: t => {
},
menu: new Menu([
'toggle_chat'
]),
component: {
data() {return Chat},
template: `
<div>
<div class="bar next_to_title" id="chat_title_bar"></div>
<ul id="chat_history" v-if="expanded">
<li v-for="msg in history">
<b v-if="msg.showAuthor()" v-bind:class="{self: msg.self}">{{ msg.author }}:</b>
<span class="text" v-bind:style="{color: msg.hex || 'inherit'}" v-html="msg.html"></span>
<span class="timestamp">{{ msg.timestamp }}</span>
</li>
</ul>
<div id="chat_bar">
<input type="text" id="chat_input" class="dark_bordered f_left" maxlength="512">
<i class="material-icons" onclick="Chat.send()">send</i>
</div>
</div>
`
}
})
BarItems.toggle_chat.toElement('#chat_title_bar')
})

View File

@ -167,6 +167,10 @@ const Interface = {
status_bar: {},
Panels: {}
}
Interface.panel_definers = []
Interface.definePanels = function(callback) {
Interface.panel_definers.push(callback);
}
//Misc
function unselectInterface(event) {

View File

@ -181,36 +181,7 @@ function setupPanels() {
}
})
//Panels
Interface.Panels.uv = new Panel({
id: 'uv',
icon: 'photo_size_select_large',
condition: {modes: ['edit', 'paint']},
toolbars: {
bottom: Toolbars.main_uv
},
onResize: function() {
let size = limitNumber($(this.node).width()-10, 64, 1200)
size = Math.floor(size/16)*16
main_uv.setSize(size)
}
})
Interface.Panels.textures = new Panel({
id: 'textures',
icon: 'fas.fa-images',
condition: {modes: ['edit', 'paint']},
toolbars: {
head: Toolbars.texturelist
},
menu: new Menu([
'import_texture',
'create_texture',
'reload_textures',
'change_textures_folder',
'save_textures'
])
})
Interface.Panels.element = new Panel({
id: 'element',
icon: 'fas.fa-cube',
@ -242,277 +213,12 @@ function setupPanels() {
`
}
})
Interface.Panels.outliner = new Panel({
id: 'outliner',
icon: 'list_alt',
condition: {modes: ['edit', 'paint', 'animate']},
toolbars: {
head: Toolbars.outliner
},
onResize: t => {
getAllOutlinerObjects().forEach(o => o.updateElement())
},
menu: new Menu([
'add_cube',
'add_group',
'_',
'sort_outliner',
'select_all',
'collapse_groups',
'element_colors',
'outliner_toggle'
])
})
Interface.Panels.chat = new Panel({
id: 'chat',
icon: 'chat',
condition: {method() {return EditSession.active}},
toolbars: {},
onResize: t => {
},
menu: new Menu([
'toggle_chat'
]),
component: {
data() {return Chat},
template: `
<div>
<div class="bar next_to_title" id="chat_title_bar"></div>
<ul id="chat_history" v-if="expanded">
<li v-for="msg in history">
<b v-if="msg.showAuthor()" v-bind:class="{self: msg.self}">{{ msg.author }}:</b>
<span class="text" v-bind:style="{color: msg.hex || 'inherit'}" v-html="msg.html"></span>
<span class="timestamp">{{ msg.timestamp }}</span>
</li>
</ul>
<div id="chat_bar">
<input type="text" id="chat_input" class="dark_bordered f_left" maxlength="512">
<i class="material-icons" onclick="Chat.send()">send</i>
</div>
</div>
`
Interface.panel_definers.forEach((definer) => {
if (typeof definer === 'function') {
definer()
}
})
BarItems.toggle_chat.toElement('#chat_title_bar')
Interface.Panels.animations = new Panel({
id: 'animations',
icon: 'movie',
condition: {modes: ['animate']},
toolbars: {
head: Toolbars.animations
},
component: {
name: 'panel-animations',
data() { return {
animations: Animator.animations,
files_folded: {}
}},
methods: {
toggle(key) {
this.files_folded[key] = !this.files_folded[key];
this.$forceUpdate();
},
saveFile(key, file) {
if (key && isApp) {
file.animations.forEach(animation => {
animation.save();
})
} else {
}
},
addAnimation(path) {
let other_animation = Animation.all.find(a => a.path == path)
console.log(path, other_animation)
new Animation({
name: other_animation && other_animation.name.replace(/\w+$/, 'new'),
path
}).add(true).propertiesDialog()
}
},
computed: {
files() {
let files = {};
this.animations.forEach(animation => {
let key = animation.path || '';
if (!files[key]) files[key] = {
animations: [],
name: animation.path ? pathToName(animation.path, true) : 'Unsaved',
saved: true
};
if (!animation.saved) files[key].saved = false;
files[key].animations.push(animation);
})
return files;
}
},
template: `
<div>
<div class="toolbar_wrapper animations"></div>
<ul id="animations_list" class="list">
<li v-for="(file, key) in files" :key="key" class="animation_file">
<div class="animation_file_head" v-on:click.stop="toggle(key)">
<i v-on:click.stop="toggle(key)" class="icon-open-state fa" :class=\'{"fa-angle-right": files_folded[key], "fa-angle-down": !files_folded[key]}\'></i>
<label>{{ file.name }}</label>
<div class="in_list_button" v-if="!file.saved" v-on:click.stop="saveFile(key, file)">
<i class="material-icons">save</i>
</div>
<div class="in_list_button" v-on:click.stop="addAnimation(key)">
<i class="material-icons">add</i>
</div>
</div>
<ul v-if="!files_folded[key]">
<li
v-for="animation in file.animations"
v-bind:class="{ selected: animation.selected }"
v-bind:anim_id="animation.uuid"
class="animation"
v-on:click.stop="animation.select()"
v-on:dblclick.stop="animation.propertiesDialog()"
:key="animation.uuid"
@contextmenu.prevent.stop="animation.showContextMenu($event)"
>
<i class="material-icons">movie</i>
<label>{{ animation.name }}</label>
<div class="in_list_button" v-bind:class="{unclickable: animation.saved}" v-on:click.stop="animation.save()">
<i v-if="animation.saved" class="material-icons">check_circle</i>
<i v-else class="material-icons">save</i>
</div>
<div class="in_list_button" v-on:click.stop="animation.togglePlayingState()">
<i v-if="animation.playing" class="fa_big far fa-play-circle"></i>
<i v-else class="fa_big far fa-circle"></i>
</div>
</li>
</ul>
</li>
</ul>
</div>
`
}
})
console.log(Timeline)
Interface.Panels.keyframe = new Panel({
id: 'keyframe',
icon: 'timeline',
condition: {modes: ['animate']},
toolbars: {
head: Toolbars.keyframe
},
component: {
name: 'panel-keyframe',
components: {VuePrismEditor},
data() { return {
keyframes: Timeline.selected
}},
methods: {
updateInput(axis, value) {
console.log(this.keyframes[0].x)
updateKeyframeValue(axis, value)
},
getKeyframeInfos() {
let list = [tl('timeline.'+this.channel)];
if (this.keyframes.length > 1) list.push(this.keyframes.length);
/*if (this.keyframes[0].color >= 0) {
list.push(tl(`cube.color.${markerColors[this.keyframes[0].color].name}`))
}*/
return list.join(', ')
}
},
computed: {
channel() {
var channel = false;
for (var kf of this.keyframes) {
if (channel === false) {
channel = kf.channel
} else if (channel !== kf.channel) {
channel = false
break;
}
}
return channel;
}
},
template: `
<div>
<div class="toolbar_wrapper keyframe"></div>
<template v-if="channel != false">
<p id="keyframe_type_label">{{ tl('panel.keyframe.type', [getKeyframeInfos()]) }}</p>
<div class="bar flex" id="keyframe_bar_x" v-if="keyframes[0].animator instanceof BoneAnimator">
<label class="color_x" style="font-weight: bolder">X</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" :value="keyframes[0].x.toString()" v-model="keyframes[0].x" @change="updateInput('x', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar flex" id="keyframe_bar_y" v-if="keyframes[0].animator instanceof BoneAnimator">
<label class="color_y" style="font-weight: bolder">Y</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" :value="keyframes[0].y.toString()" v-model="keyframes[0].y" @change="updateInput('y', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar flex" id="keyframe_bar_z" v-if="keyframes[0].animator instanceof BoneAnimator">
<label class="color_z" style="font-weight: bolder">Z</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" :value="keyframes[0].z.toString()" v-model="keyframes[0].z" @change="updateInput('z', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar flex" id="keyframe_bar_effect" v-if="channel == 'particle' || channel == 'sound'">
<label>{{ tl('data.effect') }}</label>
<input type="text" class="dark_bordered code keyframe_input tab_target" v-model="keyframes[0].effect" @input="updateInput('effect', $event)">
</div>
<div class="bar flex" id="keyframe_bar_locator" v-if="channel == 'particle'">
<label>{{ tl('data.locator') }}</label>
<input @focus="focus()" @focusout="focusout()" type="text" class="dark_bordered code keyframe_input tab_target" v-model="keyframes[0].locator" @input="updateInput('locator', $event)">
</div>
<div class="bar flex" id="keyframe_bar_script" v-if="channel == 'particle'">
<label>{{ tl('timeline.pre_effect_script') }}</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" v-model="keyframes[0].script" @change="updateInput('script', $event)" language="molang" :line-numbers="false" />
</div>
<div class="bar" id="keyframe_bar_instructions" v-if="channel == 'timeline'">
<label>{{ tl('timeline.timeline') }}</label>
<vue-prism-editor class="molang_input dark_bordered keyframe_input tab_target" v-model="keyframes[0].instructions" @change="updateInput('instructions', $event)" language="molang" :line-numbers="false" />
</div>
</template>
</div>
`
}
})
Interface.Panels.variable_placeholders = new Panel({
id: 'variable_placeholders',
icon: 'fas.fa-stream',
condition: {modes: ['animate']},
growable: true,
toolbars: {
},
component: {
name: 'panel-placeholders',
components: {VuePrismEditor},
data() { return {
text: ''
}},
template: `
<div style="flex-grow: 1; display: flex; flex-direction: column;">
<p>{{ tl('panel.variable_placeholders.info') }}</p>
<vue-prism-editor
id="var_placeholder_area"
class="molang_input dark_bordered tab_target"
v-model="text"
language="molang"
:line-numbers="false"
style="flex-grow: 1;"
onkeyup="Animator.preview()"
/>
</div>
`
}
})
Interface.Panels.display = new Panel({
id: 'display',
icon: 'tune',
condition: {modes: ['display']},
toolbars: {
head: Toolbars.display
}
})
Interface.data.left_bar.forEach((id) => {
if (Interface.Panels[id]) {
@ -536,6 +242,7 @@ function setupPanels() {
}
function setActivePanel(panel) {
Prop.active_panel = panel
}

View File

@ -69,7 +69,7 @@ class ModelProject {
Prop.added_models = 0;
Canvas.updateAll();
Outliner.vue.$forceUpdate();
texturelist.$forceUpdate();
Interface.Panels.textures.inside_vue.$forceUpdate();
Undo.history.empty();
Undo.index = 0;
Undo.current_save = null;
@ -232,7 +232,7 @@ BARS.defineActions(function() {
Texture.all.forEach((tex, i) => {
tex.visible = i < 3
})
texturelist.$forceUpdate()
Interface.Panels.textures.inside_vue.$forceUpdate()
Canvas.updateLayeredTextures();
}
}

View File

@ -905,25 +905,6 @@ function toggleCubeProperty(key) {
}
onVueSetup(function() {
Outliner.vue = new Vue({
el: '#cubes_list',
data: {
option: {
root: {
name: 'Model',
isParent: true,
isOpen: true,
selected: false,
onOpened: function () {},
select: function() {},
children: Outliner.root
}
}
}
})
})
BARS.defineActions(function() {
new Action('outliner_toggle', {
icon: 'view_stream',
@ -1056,3 +1037,70 @@ BARS.defineActions(function() {
click: function () {selectAll()}
})
})
Interface.definePanels(function() {
Interface.Panels.outliner = new Panel({
id: 'outliner',
icon: 'list_alt',
condition: {modes: ['edit', 'paint', 'animate']},
toolbars: {
head: Toolbars.outliner
},
growable: true,
onResize: t => {
getAllOutlinerObjects().forEach(o => o.updateElement())
},
component: {
name: 'panel-keyframe',
components: {VuePrismEditor},
data() { return {
root: {
name: 'Model',
isParent: true,
isOpen: true,
selected: false,
onOpened: function () {},
select: function() {},
children: Outliner.root
}
}},
methods: {
openMenu(event) {
Interface.Panels.outliner.menu.show(event)
}
},
template: `
<div>
<div class="toolbar_wrapper outliner"></div>
<ul id="cubes_list" class="list" @contextmenu.stop.prevent="openMenu($event)">
<vue-tree :root="root"></vue-tree>
</ul>
</div>
`
},
menu: new Menu([
'add_cube',
'add_group',
'_',
'sort_outliner',
'select_all',
'collapse_groups',
'element_colors',
'outliner_toggle'
])
})
Outliner.vue = Interface.Panels.outliner.inside_vue;
$('#cubes_list').droppable({
greedy: true,
accept: 'div.outliner_object',
tolerance: 'pointer',
hoverClass: 'drag_hover',
drop: function(event, ui) {
var item = Outliner.root.findRecursive('uuid', $(ui.draggable).parent().attr('id'))
console.log('drop')
dropOutlinerObjects(item, undefined, event)
}
})
})

View File

@ -77,11 +77,14 @@
Vue.component('vue-tree-item', VueTreeItem);
var VueTree = Vue.extend({
template: '<div class="vue-tree"><ul>' +
'<tree-item :node.sync="option.root"></tree-item>' +
'</ul></div>',
template: `
<div class="vue-tree">
<ul>
<tree-item :node.sync="root"></tree-item>
</ul>
</div>`,
props: {
option: {
root: {
type: Object
}
},
@ -90,4 +93,5 @@
}
});
Vue.component('vue-tree', VueTree);
})();
})();

View File

@ -44,7 +44,7 @@ function colorDistance(color1, color2) {
'#ea323c','#c42430','#891e2b','#571c27',
]
}
onVueSetup(() => {
Interface.definePanels(() => {
ColorPanel = Interface.Panels.color = new Panel({
id: 'color',
icon: 'palette',

View File

@ -1230,16 +1230,6 @@ TextureAnimator = {
}
}
onVueSetup(function() {
texturelist = new Vue({
el: '#texture_list',
data: {
textures: Texture.all
}
})
texturelist._data.elements = textures
})
BARS.defineActions(function() {
new Action('import_texture', {
icon: 'library_add',
@ -1346,3 +1336,78 @@ BARS.defineActions(function() {
}
})
})
Interface.definePanels(function() {
Interface.Panels.textures = new Panel({
id: 'textures',
icon: 'fas.fa-images',
growable: true,
condition: {modes: ['edit', 'paint']},
toolbars: {
head: Toolbars.texturelist
},
component: {
name: 'panel-keyframe',
components: {VuePrismEditor},
data() { return {
textures: Texture.all
}},
methods: {
openMenu(event) {
Interface.Panels.textures.menu.show(event)
}
},
template: `
<div>
<div class="toolbar_wrapper texturelist"></div>
<ul id="texture_list" class="list" @contextmenu.stop.prevent="openMenu($event)">
<li
v-for="texture in textures"
v-bind:class="{ selected: texture.selected, particle: texture.particle}"
v-bind:texid="texture.uuid"
:key="texture.uuid"
class="texture"
v-on:click.stop="texture.select($event)"
v-on:dblclick="texture.openMenu($event)"
@contextmenu.prevent.stop="texture.showContextMenu($event)"
>
<div class="texture_icon_wrapper">
<img v-bind:texid="texture.id" v-bind:src="texture.source" class="texture_icon" width="48px" alt="" v-if="texture.show_icon" />
<i class="material-icons texture_error" v-bind:title="texture.getErrorMessage()" v-if="texture.error">error_outline</i>
<i class="texture_movie fa fa_big fa-film" title="Animated Texture" v-if="texture.frameCount > 1"></i>
</div>
<div class="texture_description_wrapper">
<div class="texture_name">{{ texture.name }}</div>
<div class="texture_res">{{ texture.error
? texture.getErrorMessage()
: texture.width + ' x ' + texture.height + 'px'
}}</div>
</div>
<i class="material-icons texture_visibility_icon" v-if="texture.particle">bubble_chart</i>
<i class="material-icons texture_particle_icon clickable"
v-bind:class="{icon_off: !texture.visible}"
v-if="Project.layered_textures"
@click="texture.toggleVisibility()"
@dblclick.stop
>
{{ texture.visible ? 'visibility' : 'visibility_off' }}
</i>
<i class="material-icons texture_save_icon" v-bind:class="{clickable: !texture.saved}" @click="texture.save()">
<template v-if="texture.saved">check_circle</template>
<template v-else>save</template>
</i>
</li>
</ul>
</div>
`
},
menu: new Menu([
'import_texture',
'create_texture',
'reload_textures',
'change_textures_folder',
'save_textures'
])
})
})

View File

@ -2246,3 +2246,21 @@ BARS.defineActions(function() {
}
})
})
Interface.definePanels(function() {
Interface.Panels.uv = new Panel({
id: 'uv',
icon: 'photo_size_select_large',
selection_only: true,
condition: {modes: ['edit', 'paint']},
toolbars: {
bottom: Toolbars.main_uv
},
onResize: function() {
let size = limitNumber($(this.node).width()-10, 64, 1200)
size = Math.floor(size/16)*16
main_uv.setSize(size)
}
})
})