Panel fixes and resizable panels

This commit is contained in:
JannisX11 2022-03-03 21:13:30 +01:00
parent a3fe55783f
commit c74e30ad05
18 changed files with 490 additions and 281 deletions

View File

@ -271,6 +271,7 @@
.toolbar {
width: 100%;
overflow: hidden;
flex-shrink: 0;
}
.toolbar > .content {
display: flex;
@ -427,7 +428,7 @@
color: var(--color-bright_ui_text);
margin-top: 30px;
white-space: nowrap;
z-index: 18;
z-index: 20;
box-shadow: 0 0.4px 3.5px rgba(0, 0, 0, 0.6);
pointer-events: none;
visibility: hidden;

View File

@ -18,6 +18,8 @@
position: absolute;
border: 1px solid var(--color-accent);
box-shadow: 0 0 10px rgb(0 0 0 / 40%);
box-sizing: content-box;
z-index: 14;
}
h3.panel_handle {
display: flex;
@ -89,6 +91,71 @@
bottom: 0px;
}
/* Panel Resize Lines */
.panel:not(.floating) > .panel_resize_handle_wrapper {
display: none;
}
.panel_resize_side {
width: 6px;
height: 6px;
position: absolute;
top: 3px;
bottom: 3px;
left: 3px;
right: 3px;
}
.panel_resize_corner {
width: 8px;
height: 8px;
position: absolute;
}
.panel_resize_side.resize_top {
cursor: ns-resize;
top: -3px;
bottom: unset;
width: auto;
}
.panel_resize_side.resize_bottom {
cursor: ns-resize;
top: unset;
bottom: -3px;
width: auto;
}
.panel_resize_side.resize_left {
cursor: ew-resize;
left: -3px;
right: unset;
height: auto;
}
.panel_resize_side.resize_right {
cursor: ew-resize;
left: unset;
right: -3px;
height: auto;
}
.panel_resize_corner.resize_top_left {
cursor: nw-resize;
left: -4px;
top: -4px;
}
.panel_resize_corner.resize_top_right {
cursor: ne-resize;
right: -4px;
top: -4px;
}
.panel_resize_corner.resize_bottom_left {
cursor: sw-resize;
left: -4px;
bottom: -4px;
}
.panel_resize_corner.resize_bottom_right {
cursor: se-resize;
right: -4px;
bottom: -4px;
}
/*Display*/
.tabs_small input[type="radio"]:checked+label {
@ -734,7 +801,7 @@
/*Timeline*/
#timeline {
#panel_timeline {
display: block;
height: 300px;
background-color: var(--color-ui);
@ -824,7 +891,7 @@
margin-left: -2px;
}
#timeline .keyframe {
#panel_timeline .keyframe {
position: absolute;
margin-left: -6px;
z-index: 3;
@ -832,19 +899,19 @@
width: 13.5px;
height: 23px;
}
#timeline .keyframe i {
#panel_timeline .keyframe i {
margin-top: 3px;
font-size: 16px;
margin-left: -1px;
pointer-events: none;
display: block;
}
#timeline .keyframe i.keyframe_icon_smaller {
#panel_timeline .keyframe i.keyframe_icon_smaller {
font-size: 11pt;
margin-top: 4px;
margin-left: -1px;
}
#timeline .keyframe i.keyframe_icon_step {
#panel_timeline .keyframe i.keyframe_icon_step {
font-size: 16pt;
margin-top: 4px;
margin-left: -2px;
@ -859,11 +926,11 @@
font-size: 6pt;
color: var(--color-grid);
}
#timeline .keyframe.selected i {
#panel_timeline .keyframe.selected i {
color: var(--color-accent) !important;
z-index: 4;
}
#timeline .keyframe:hover {
#panel_timeline .keyframe:hover {
z-index: 4;
}
@ -1384,7 +1451,7 @@
.panel .bar.next_to_title {
margin-top: -34px;
margin-right: 32px;
margin-right: 54px;
position: relative;
float: right;
pointer-events: none;

View File

@ -353,6 +353,7 @@
}
div {
cursor: default;
box-sizing: border-box;
}
a {
text-decoration: none;

View File

@ -825,7 +825,7 @@ const Animator = {
Animator.motion_trail.no_export = true;
if (!Animator.timeline_node) {
Animator.timeline_node = $('#timeline').get(0)
Animator.timeline_node = Panels.timeline.node;
}
updateInterface()
Toolbars.element_origin.toPlace('bone_origin')
@ -1714,6 +1714,12 @@ Interface.definePanels(function() {
icon: 'movie',
growable: true,
condition: {modes: ['animate']},
default_position: {
slot: 'left_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
head: Toolbars.animations
},
@ -1970,6 +1976,12 @@ Interface.definePanels(function() {
icon: 'fas.fa-stream',
condition: {modes: ['animate']},
growable: true,
default_position: {
slot: 'left_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
},
component: {

View File

@ -947,6 +947,12 @@ Interface.definePanels(function() {
new Panel('keyframe', {
icon: 'timeline',
condition: {modes: ['animate']},
default_position: {
slot: 'left_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
head: Toolbars.keyframe
},

View File

@ -584,13 +584,15 @@ Interface.definePanels(() => {
*/
Timeline.panel = new Panel('timeline', {
icon: 'timeline',
condition: {modes: ['display']},
condition: {modes: ['animate']},
default_position: {
slot: 'bottom',
height: 200,
float_position: [100, 400],
float_size: [600, 300],
height: 260,
},
toolbars: {
head: Toolbars.display
timeline: Toolbars.timeline
},
component: {
name: 'panel-timeline',
@ -684,7 +686,7 @@ Interface.definePanels(() => {
if (!this._isMounted) return;
this.timecodes.empty();
let second_fractions = settings.timecode_frame_number.value ? 1/Timeline.getStep() : 100;
let timeline_container_width = document.getElementById('timeline').clientWidth - this.head_width;
let timeline_container_width = Panels.timeline.node.clientWidth - this.head_width;
this.length = Timeline.getMaxLength();
var step = 1

View File

@ -1653,6 +1653,12 @@ Interface.definePanels(function() {
new Panel('display', {
icon: 'tune',
condition: {modes: ['display']},
default_position: {
slot: 'left_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
head: Toolbars.display
},

View File

@ -507,6 +507,12 @@ Interface.definePanels(function() {
new Panel('chat', {
icon: 'chat',
condition: {method() {return Project.EditSession && Project.EditSession.active}},
default_position: {
slot: 'right_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {},
onResize: t => {
},

View File

@ -318,6 +318,16 @@ function buildComponent(dialog) {
dialog.component.name = 'dialog-content'
dialog.content_vue = new Vue(dialog.component).$mount(mount.get(0));
}
function getStringWidth(string, size) {
var a = $('<label style="position: absolute">'+string+'</label>')
if (size && size !== 16) {
a.css('font-size', size+'pt')
}
$('body').append(a.css('visibility', 'hidden'))
var width = a.width()
a.detach()
return width;
};
class DialogSidebar {
constructor(options, dialog) {
@ -729,3 +739,43 @@ window.Dialog = class Dialog {
}
})()
// Legacy Dialogs
function showDialog(dialog) {
var obj = $('.dialog#'+dialog)
$('.dialog').hide()
if (open_menu) {
open_menu.hide()
}
$('#blackout').show()
obj.show()
open_dialog = dialog
open_interface = {
confirm() {
$('dialog#'+open_dialog).find('.confirm_btn:not([disabled])').trigger('click');
},
cancel() {
$('dialog#'+open_dialog).find('.cancel_btn:not([disabled])').trigger('click');
}
}
Prop.active_panel = 'dialog'
//Draggable
if (obj.hasClass('draggable')) {
obj.draggable({
handle: ".dialog_handle",
containment: '#page_wrapper'
})
var x = (window.innerWidth-obj.outerWidth()) / 2;
obj.css('left', x+'px')
obj.css('max-height', (window.innerHeight-128)+'px')
}
}
function hideDialog() {
$('#blackout').hide()
$('.dialog').hide()
open_dialog = false;
open_interface = false;
Prop.active_panel = undefined
}

View File

@ -72,10 +72,10 @@ const Interface = {
right_bar_width: 314,
quad_view_x: 50,
quad_view_y: 50,
timeline_height: 260,
timeline_head: Blockbench.isMobile ? 140 : 196,
left_bar: ['uv', 'textures', 'display', 'animations', 'keyframe', 'variable_placeholders'],
right_bar: ['element', 'bone', 'color', 'skin_pose', 'outliner', 'chat']
right_bar: ['element', 'bone', 'color', 'skin_pose', 'outliner', 'chat'],
panels: {}
},
get left_bar_width() {
return Prop.show_left_bar ? Interface.data.left_bar_width : 0;
@ -195,21 +195,28 @@ const Interface = {
top: new ResizeLine('top', {
horizontal: true,
condition() {return Interface.getTopPanel()},
get() {return Interface.getTopPanel().position_data.height},
get() {
let panel = Interface.getTopPanel();
return panel.folded ? panel.handle.clientHeight : panel.position_data.height;
},
set(o, diff) {
Interface.getTopPanel().position_data.height = limitNumber(o - diff, 150, document.body.clientHeight-120);
Interface.getTopPanel().update();
let panel = Interface.getTopPanel();
panel.position_data.height = limitNumber(o + diff, 150, document.body.clientHeight-120);
panel.update();
},
position() {this.setPosition({
left: Interface.left_bar_width+2,
right: Interface.right_bar_width+2,
top: this.get()
top: this.get() + document.getElementById('work_screen').offsetTop - document.getElementById('page_wrapper').offsetTop
})}
}),
bottom: new ResizeLine('bottom', {
horizontal: true,
condition() {return Interface.getBottomPanel()},
get() {return Interface.getBottomPanel().position_data.height},
get() {
let panel = Interface.getBottomPanel();
return panel.folded ? panel.handle.clientHeight : panel.position_data.height;
},
set(o, diff) {
Interface.getBottomPanel().position_data.height = limitNumber(o - diff, 150, document.body.clientHeight-120);
Interface.getBottomPanel().update();
@ -217,7 +224,7 @@ const Interface = {
position() {this.setPosition({
left: Interface.left_bar_width+2,
right: Interface.right_bar_width+2,
top: document.getElementById('work_screen').clientHeight - this.get()
top: document.getElementById('work_screen').clientHeight - document.getElementById('status_bar').clientHeight - this.get()
})}
}),
timeline_head: new ResizeLine('timeline_head', {
@ -229,11 +236,14 @@ const Interface = {
value = Math.snapToValues(value, [Interface.default_data.timeline_head], 12);
Interface.data.timeline_head = Timeline.vue._data.head_width = value;
},
position() {this.setPosition({
left: Interface.left_bar_width+2 + Interface.data.timeline_head,
top: $('#timeline').offset().top + 60,
bottom: document.getElementById('status_bar').clientHeight + 12,
})}
position() {
let offset = $(Panels.timeline.node).offset();
this.setPosition({
left: offset.left + 2 + Interface.data.timeline_head,
top: offset.top + 60,
bottom: offset.top + 60 + Panels.timeline.node.clientHeight
})
}
})
},
status_bar: {},
@ -253,6 +263,7 @@ Interface.definePanels = function(callback) {
(function() {
Interface.data = $.extend(true, {}, Interface.default_data)
var interface_data = localStorage.getItem('interface_data')
if (!interface_data) return;
try {
interface_data = JSON.parse(interface_data)
let original_left_bar, original_right_bar;
@ -278,10 +289,11 @@ Interface.definePanels = function(callback) {
Interface.data.right_bar.splice(i, 0, panel);
})
}
console.log(interface_data)
$.extend(true, Interface.data, interface_data)
console.log(Interface.data)
} catch (err) {}
Interface.data.panels.uv.slot = interface_data.panels.uv.slot;
} catch (err) {
console.error(err);
}
})()
//Misc
@ -365,7 +377,7 @@ function setupInterface() {
$('#texture_list').click(function(){
unselectTextures()
})
$('#timeline').mousedown((event) => {
$(Panels.timeline.node).mousedown((event) => {
setActivePanel('timeline');
})
$(document).on('mousedown touchstart', unselectInterface)
@ -523,55 +535,6 @@ function setZoomLevel(mode) {
}
}
//Dialogs
function showDialog(dialog) {
var obj = $('.dialog#'+dialog)
$('.dialog').hide()
if (open_menu) {
open_menu.hide()
}
$('#blackout').show()
obj.show()
open_dialog = dialog
open_interface = {
confirm() {
$('dialog#'+open_dialog).find('.confirm_btn:not([disabled])').trigger('click');
},
cancel() {
$('dialog#'+open_dialog).find('.cancel_btn:not([disabled])').trigger('click');
}
}
Prop.active_panel = 'dialog'
//Draggable
if (obj.hasClass('draggable')) {
obj.draggable({
handle: ".dialog_handle",
containment: '#page_wrapper'
})
var x = (window.innerWidth-obj.outerWidth()) / 2;
obj.css('left', x+'px')
obj.css('max-height', (window.innerHeight-128)+'px')
}
}
function hideDialog() {
$('#blackout').hide()
$('.dialog').hide()
open_dialog = false;
open_interface = false;
Prop.active_panel = undefined
}
function getStringWidth(string, size) {
var a = $('<label style="position: absolute">'+string+'</label>')
if (size && size !== 16) {
a.css('font-size', size+'pt')
}
$('body').append(a.css('visibility', 'hidden'))
var width = a.width()
a.detach()
return width;
};
//UI Edit
function setProgressBar(id, val, time) {
if (!id || id === 'main') {

View File

@ -18,25 +18,13 @@ class Panel {
this.onFold = data.onFold;
this.toolbars = data.toolbars || {};
if (!Interface.data.panels[this.id]) {
Interface.data.panels[this.id] = {
slot: data.default_side ? (data.default_side+'_bar') : 'left',
float_position: [0, 0],
float_size: [300, 300],
height: 300,
}
}
if (!Interface.data.panels[this.id]) Interface.data.panels[this.id] = {};
this.position_data = Interface.data.panels[this.id];
if (data.default_position) {
Merge.string(this.position_data, data.default_position, 'slot');
Merge.arrayVector2(this.position_data, data.default_position, 'float_position');
Merge.arrayVector2(this.position_data, data.default_position, 'float_size');
Merge.string(this.position_data, data.default_position, 'height');
}
let defaultp = data.default_position || 0;
if (!this.position_data.slot) this.position_data.slot = defaultp.slot || (data.default_side ? (data.default_side+'_bar') : 'left_bar');
if (!this.position_data.float_position) this.position_data.float_position = defaultp.float_position || [0, 0];
if (!this.position_data.float_size) this.position_data.float_size = defaultp.float_size || [300, 300];
if (!this.position_data.height) this.position_data.height = defaultp.height || 300;
this.handle = Interface.createElement('h3', {class: 'panel_handle'}, Interface.createElement('label', {}, this.name));
this.node = Interface.createElement('div', {class: 'panel', id: `panel_${this.id}`}, this.handle);
@ -44,19 +32,6 @@ class Panel {
if (this.selection_only) this.node.classList.add('selection_only');
if (this.growable) this.node.classList.add('grow');
/*
let bar = $(`.sidebar#${data.default_side||'left'}_bar`);
if (data.insert_before && bar.find(`> .panel#${data.insert_before}`).length) {
$(this.node).insertBefore(bar.find(`> .panel#${data.insert_before}`));
} else if (data.insert_after && bar.find(`> .panel#${data.insert_after}`).length) {
$(this.node).insertAfter(bar.find(`> .panel#${data.insert_after}`));
} else {
bar.append(this.node);
}*/
// Toolbars
for (let key in this.toolbars) {
let toolbar = this.toolbars[key];
@ -71,9 +46,9 @@ class Panel {
if (data.component) {
let component_mount = Interface.createElement('div', {class: 'panel_vue_wrapper'});
let component_mount = Interface.createElement('div');
this.node.append(component_mount);
let component_mounted = data.component.mounted;
let onmounted = data.component.mounted;
data.component.mounted = function() {
Vue.nextTick(() => {
@ -86,35 +61,14 @@ class Panel {
}
})
if (typeof component_mounted == 'function') {
component_mounted.call(this);
if (typeof onmounted == 'function') {
onmounted.call(this);
}
updateInterfacePanels()
})
}
this.vue = this.inside_vue = new Vue(data.component).$mount(component_mount);
// TODO: On mounted, add toolbars to anchors in Vue node, probably updateInterfacePanels()
/*
data.component.name = 'inside-vue'
this.vue = new Vue({
components: {
'inside-vue': data.component
},
template: `<div class="panel ${this.selection_only ? 'selection_only' : ''} ${this.growable ? 'grow' : ''}" id="${this.id}">
<inside-vue class="panel_inside" ref="inside"></inside-vue>
</div>`,
mounted() {
Vue.nextTick(() => {
updateInterfacePanels()
})
}
}).$mount(this.node);
this.inside_vue = this.vue.$refs.inside;*/
scope.vue.$el.classList.add('panel_vue_wrapper');
}
if (!Blockbench.isMobile) {
@ -124,24 +78,29 @@ class Panel {
snap_button.addEventListener('click', (e) => {
new Menu([
{
name: 'Right Sidebar',
click: e2 => this.moveTo('right_bar')
name: 'Left Sidebar',
icon: 'align_horizontal_left',
click: () => this.moveTo('left_bar')
},
{
name: 'Left Sidebar',
click: e2 => this.moveTo('left_bar')
name: 'Right Sidebar',
icon: 'align_horizontal_right',
click: () => this.moveTo('right_bar')
},
{
name: 'Top',
click: e2 => this.moveTo('top')
icon: 'align_vertical_top',
click: () => this.moveTo('top')
},
{
name: 'Bottom',
click: e2 => this.moveTo('bottom')
icon: 'align_vertical_bottom',
click: () => this.moveTo('bottom')
},
{
name: 'Float',
click: e2 => this.moveTo('float')
icon: 'web_asset',
click: () => this.moveTo('float')
}
]).show(snap_button);
})
@ -168,6 +127,11 @@ class Panel {
}
if (!started) return;
if (this.slot !== 'float') {
this.moveTo('float');
this.moveToFront();
}
this.position_data.float_position[0] = position_before[0] + e2.clientX - e1.clientX;
this.position_data.float_position[1] = position_before[1] + e2.clientY - e1.clientY;
this.update();
@ -176,8 +140,6 @@ class Panel {
let stop = e2 => {
convertTouchEvent(e2);
this.moveTo('float')
saveSidebarOrder()
updateInterface()
@ -255,31 +217,15 @@ class Panel {
})
}
this.node.addEventListener('mousedown', event => {
setActivePanel(this.id)
setActivePanel(this.id);
this.moveToFront();
})
// Add to slot
let reference_panel = Panels[data.insert_before || data.insert_after];
this.moveTo(this.position_data.slot, reference_panel, reference_panel && !data.insert_after);
// Sort
if (Blockbench.setup_successful) {
if (Interface.data.right_bar.includes(this.id) && this.slot == 'right_bar') {
let index = Interface.data.right_bar.indexOf(this.id);
if (index == 0) {
this.moveTo('right_bar', Interface.Panels[Interface.data.right_bar[1]], true)
} else {
this.moveTo('right_bar', Interface.Panels[Interface.data.right_bar[index-1]], false)
}
} else if (Interface.data.left_bar.includes(this.id) && this.slot == 'left_bar') {
let index = Interface.data.left_bar.indexOf(this.id);
if (index == 0) {
this.moveTo('left_bar', Interface.Panels[Interface.data.left_bar[1]], true)
} else {
this.moveTo('left_bar', Interface.Panels[Interface.data.left_bar[index-1]], false)
}
}
}
Interface.Panels[this.id] = this;
}
isVisible() {
@ -296,16 +242,101 @@ class Panel {
if (this.onFold) {
this.onFold();
}
this.update();
return this;
}
setupFloatHandles() {
let sides = [
Interface.createElement('div', {class: 'panel_resize_side resize_top'}),
Interface.createElement('div', {class: 'panel_resize_side resize_bottom'}),
Interface.createElement('div', {class: 'panel_resize_side resize_left'}),
Interface.createElement('div', {class: 'panel_resize_side resize_right'}),
];
let corners = [
Interface.createElement('div', {class: 'panel_resize_corner resize_top_left'}),
Interface.createElement('div', {class: 'panel_resize_corner resize_top_right'}),
Interface.createElement('div', {class: 'panel_resize_corner resize_bottom_left'}),
Interface.createElement('div', {class: 'panel_resize_corner resize_bottom_right'}),
];
let resize = (e1, direction_x, direction_y) => {
let position_before = this.position_data.float_position.slice();
let size_before = this.position_data.float_size.slice();
let started = false;
let drag = e2 => {
convertTouchEvent(e2);
if (!started && (Math.pow(e2.clientX - e1.clientX, 2) + Math.pow(e2.clientX - e1.clientX, 2)) > 15) {
started = true;
}
if (!started) return;
this.position_data.float_size[0] = size_before[0] + (e2.clientX - e1.clientX) * direction_x;
this.position_data.float_size[1] = size_before[1] + (e2.clientY - e1.clientY) * direction_y;
if (direction_x == -1) this.position_data.float_position[0] = position_before[0] - this.position_data.float_size[0] + size_before[0];
if (direction_y == -1) this.position_data.float_position[1] = position_before[1] - this.position_data.float_size[1] + size_before[1];
if (this.onResize) {
this.onResize()
}
this.update();
}
let stop = e2 => {
convertTouchEvent(e2);
removeEventListeners(document, 'mousemove touchmove', drag);
removeEventListeners(document, 'mouseup touchend', stop);
}
addEventListeners(document, 'mousemove touchmove', drag);
addEventListeners(document, 'mouseup touchend', stop);
}
addEventListeners(sides[0], 'mousedown touchstart', (event) => resize(event, 0, -1));
addEventListeners(sides[1], 'mousedown touchstart', (event) => resize(event, 0, 1));
addEventListeners(sides[2], 'mousedown touchstart', (event) => resize(event, -1, 0));
addEventListeners(sides[3], 'mousedown touchstart', (event) => resize(event, 1, 0));
addEventListeners(corners[0], 'mousedown touchstart', (event) => resize(event, -1, -1));
addEventListeners(corners[1], 'mousedown touchstart', (event) => resize(event, 1, -1));
addEventListeners(corners[2], 'mousedown touchstart', (event) => resize(event, -1, 1));
addEventListeners(corners[3], 'mousedown touchstart', (event) => resize(event, 1, 1));
let handles = Interface.createElement('div', {class: 'panel_resize_handle_wrapper'}, [...sides, ...corners]);
this.node.append(handles);
this.resize_handles = handles;
return this;
}
moveToFront() {
if (this.slot == 'float' && Panel.floating_panel_z_order[0] !== this.id) {
Panel.floating_panel_z_order.remove(this.id);
Panel.floating_panel_z_order.splice(0, 0, this.id);
let zindex = 18;
Panel.floating_panel_z_order.forEach(id => {
let panel = Panels[id];
panel.node.style.zIndex = zindex;
zindex = Math.clamp(zindex-1, 14, 19);
})
}
return this;
}
moveTo(slot, ref_panel, before = false) {
console.trace(this.id, slot)
let position_data = this.position_data;
if (slot == undefined) {
slot = ref_panel.position_data.slot;
}
this.node.classList.remove('floating');
if (slot.match(/_bar$/)) {
if (slot == 'left_bar' || slot == 'right_bar') {
if (!ref_panel && Interface.data[slot].includes(this.id)) {
let index = Interface.data[slot].indexOf(this.id);
if (index == 0) {
ref_panel = Interface.Panels[Interface.data[slot][1]];
before = true;
} else {
ref_panel = Interface.Panels[Interface.data[slot][index-1]];
before = false;
}
}
if (ref_panel instanceof Panel && ref_panel.slot == slot) {
if (before) {
$(ref_panel.node).before(this.node);
@ -313,7 +344,7 @@ class Panel {
$(ref_panel.node).after(this.node);
}
} else {
$(`#${slot}`).append(this.node);
document.getElementById(slot).append(this.node);
}
} else if (slot == 'top') {
@ -331,12 +362,12 @@ class Panel {
} else if (slot == 'float') {
document.getElementById('work_screen').append(this.node);
this.node.classList.add('floating');
this.node.style.left = position_data.float_position[0] + 'px';
this.node.style.top = position_data.float_position[1] + 'px';
this.node.style.width = position_data.float_size[0] + 'px';
this.node.style.height = position_data.float_size[1] + 'px';
if (!this.resize_handles) {
this.setupFloatHandles();
}
}
position_data.slot = slot;
this.update();
if (Panels[this.id]) {
// Only update after initial setup
@ -346,12 +377,30 @@ class Panel {
saveSidebarOrder()
updateInterface()
}
return this;
}
update() {
let show = BARS.condition(this.condition);
let work_screen = document.querySelector('div#work_screen');
let center_screen = document.querySelector('div#center');
if (show) {
$(this.node).show()
if (this.slot == 'float') {
this.position_data.float_position[0] = Math.clamp(this.position_data.float_position[0], 0, work_screen.clientWidth - this.width);
this.position_data.float_position[1] = Math.clamp(this.position_data.float_position[1], 0, work_screen.clientHeight - this.height);
this.position_data.float_size[0] = Math.clamp(this.position_data.float_size[0], 300, work_screen.clientWidth - this.position_data.float_position[0]);
this.position_data.float_size[1] = Math.clamp(this.position_data.float_size[1], 300, work_screen.clientHeight - this.position_data.float_position[1]);
this.node.style.left = this.position_data.float_position[0] + 'px';
this.node.style.top = this.position_data.float_position[1] + 'px';
this.width = this.position_data.float_size[0];
this.height = this.position_data.float_size[1];
if (this.folded) this.height = this.handle.clientHeight;
this.node.style.width = this.width + 'px';
this.node.style.height = this.height + 'px';
} else {
this.node.style.width = this.node.style.height = this.node.style.left = this.node.style.top = null;
}
if (Blockbench.isMobile) {
this.width = this.node.clientWidth;
} else if (this.slot == 'left_bar') {
@ -359,65 +408,34 @@ class Panel {
} else if (this.slot == 'right_bar') {
this.width = Interface.data.right_bar_width
}
if (this.slot == 'float') {
this.node.style.left = Math.clamp(this.position_data.float_position[0], 0, 5000);
this.node.style.top = Math.clamp(this.position_data.float_position[1], 0, 5000);
this.width = Math.clamp(this.position_data.float_position[0], 100, center_screen.clientWidth) + 'px';
this.height = Math.clamp(this.position_data.float_position[1], 100, center_screen.clientHeight) + 'px';
this.node.style.width = this.width + 'px';
this.node.style.height = this.height + 'px';
}
if (this.slot == 'top' || this.slot == 'bottom') {
this.height = Math.clamp(this.position_data.height, 30, center_screen.clientHeight);
if (this.folded) this.height = this.handle.clientHeight;
this.node.style.height = this.height + 'px';
}
if (this.onResize) this.onResize()
if (Panels[this.id] && this.onResize) this.onResize()
} else {
$(this.node).hide()
}
localStorage.setItem('interface_data', JSON.stringify(Interface.data))
return this;
}
delete() {
delete Interface.Panels[this.id];
this.node.remove()
}
}
Panel.floating_panel_z_order = [];
function setupPanels() {
//Panels
new Panel('element', {
icon: 'fas.fa-cube',
condition: !Blockbench.isMobile && {modes: ['edit', 'pose']},
selection_only: true,
toolbars: {
element_position: !Blockbench.isMobile && Toolbars.element_position,
element_size: !Blockbench.isMobile && Toolbars.element_size,
element_origin: !Blockbench.isMobile && Toolbars.element_origin,
element_rotation: !Blockbench.isMobile && Toolbars.element_rotation,
}
})
new Panel('bone', {
icon: 'fas.fa-bone',
condition: !Blockbench.isMobile && {modes: ['animate']},
selection_only: true,
component: {
template: `
<div>
<p>${ tl('panel.element.origin') }</p>
<div class="toolbar_wrapper bone_origin"></div>
</div>
`
}
})
Interface.panel_definers.forEach((definer) => {
if (typeof definer === 'function') {
definer()
}
})
updateSidebarOrder();
}
function updateInterfacePanels() {
@ -450,28 +468,32 @@ function updateInterfacePanels() {
$('.quad_canvas_wrapper.qcw_y').css('height', Interface.data.quad_view_y+'%')
$('.quad_canvas_wrapper:not(.qcw_x)').css('width', (100-Interface.data.quad_view_x)+'%')
$('.quad_canvas_wrapper:not(.qcw_y)').css('height', (100-Interface.data.quad_view_y)+'%')
$('#timeline').css('height', Interface.data.timeline_height+'px')
//$('#timeline').css('height', Interface.data.timeline_height+'px')
for (var key in Interface.Resizers) {
var resizer = Interface.Resizers[key]
resizer.update()
}
}
function updateSidebarOrder() {
['left_bar', 'right_bar'].forEach(bar => {
let bar_node = document.querySelector(`.sidebar#${bar}`);
bar_node.childNodes.forEach(panel_node => panel_node.remove());
Interface.data[bar].forEach(panel_id => {
let panel = Panels[panel_id];
if (panel && Condition(panel.condition)) {
bar_node.append(panel.node);
}
});
})
}
function setActivePanel(panel) {
Prop.active_panel = panel
}
function saveSidebarOrder() {
Interface.data.left_bar.empty();
$('#left_bar > .panel').each((i, obj) => {
let id = $(obj).attr('id');
Interface.data.left_bar.push(id);
})
Interface.data.right_bar.empty();
$('#right_bar > .panel').each((i, obj) => {
let id = $(obj).attr('id');
Interface.data.right_bar.push(id);
})
localStorage.setItem('interface_data', JSON.stringify(Interface.data))
}

View File

@ -269,6 +269,68 @@ const skin_dialog = new Dialog({
BARS.defineActions(function() {
new Action('toggle_skin_layer', {
icon: 'layers_clear',
category: 'edit',
condition: {formats: ['skin']},
click: function () {
var edited = [];
Cube.all.forEach(cube => {
if (cube.name.toLowerCase().includes('layer')) {
edited.push(cube);
}
})
if (!edited.length) return;
Undo.initEdit({elements: edited});
value = !edited[0].visibility;
edited.forEach(cube => {
cube.visibility = value;
})
Undo.finishEdit('Toggle skin layer');
Canvas.updateVisibility()
}
})
new Action({
id: 'export_minecraft_skin',
icon: 'icon-player',
category: 'file',
condition: () => Format == format && Texture.all[0],
click: function () {
Texture.all[0].save(true);
}
})
let explode_skin_model = new Toggle('explode_skin_model', {
icon: () => 'open_in_full',
category: 'edit',
condition: {formats: ['skin']},
value: false,
onChange(exploded_view) {
Undo.initEdit({elements: Cube.all, exploded_view: !exploded_view});
Cube.all.forEach(cube => {
let center = [
cube.from[0] + (cube.to[0] - cube.from[0]) / 2,
cube.from[1],
cube.from[2] + (cube.to[2] - cube.from[2]) / 2,
]
let offset = cube.name.toLowerCase().includes('leg') ? 1 : 0.5;
center.V3_multiply(exploded_view ? offset : -offset/(1+offset));
cube.from.V3_add(center);
cube.to.V3_add(center);
})
Project.exploded_view = exploded_view;
Undo.finishEdit(exploded_view ? 'Explode skin model' : 'Revert exploding skin model', {elements: Cube.all, exploded_view: exploded_view});
Canvas.updateView({elements: Cube.all, element_aspects: {geometry: true}});
this.setIcon(this.icon);
}
})
Blockbench.on('select_project', () => {
explode_skin_model.value = !!Project.exploded_view;
explode_skin_model.updateEnabledState();
})
})
Interface.definePanels(function() {
const poses = {
none: {
Head: [0, 0, 0],
@ -330,6 +392,12 @@ BARS.defineActions(function() {
new Panel('skin_pose', {
icon: 'icon-player',
condition: {modes: ['pose']},
default_position: {
slot: 'right_bar',
float_position: [0, 0],
float_size: [300, 80],
height: 80
},
component: {
data() {return {
pose: 'default'
@ -373,66 +441,6 @@ BARS.defineActions(function() {
`
}
})
new Action('toggle_skin_layer', {
icon: 'layers_clear',
category: 'edit',
condition: {formats: ['skin']},
click: function () {
var edited = [];
Cube.all.forEach(cube => {
if (cube.name.toLowerCase().includes('layer')) {
edited.push(cube);
}
})
if (!edited.length) return;
Undo.initEdit({elements: edited});
value = !edited[0].visibility;
edited.forEach(cube => {
cube.visibility = value;
})
Undo.finishEdit('Toggle skin layer');
Canvas.updateVisibility()
}
})
new Action({
id: 'export_minecraft_skin',
icon: 'icon-player',
category: 'file',
condition: () => Format == format && Texture.all[0],
click: function () {
Texture.all[0].save(true);
}
})
let explode_skin_model = new Toggle('explode_skin_model', {
icon: () => 'open_in_full',
category: 'edit',
condition: {formats: ['skin']},
value: false,
onChange(exploded_view) {
Undo.initEdit({elements: Cube.all, exploded_view: !exploded_view});
Cube.all.forEach(cube => {
let center = [
cube.from[0] + (cube.to[0] - cube.from[0]) / 2,
cube.from[1],
cube.from[2] + (cube.to[2] - cube.from[2]) / 2,
]
let offset = cube.name.toLowerCase().includes('leg') ? 1 : 0.5;
center.V3_multiply(exploded_view ? offset : -offset/(1+offset));
cube.from.V3_add(center);
cube.to.V3_add(center);
})
Project.exploded_view = exploded_view;
Undo.finishEdit(exploded_view ? 'Explode skin model' : 'Revert exploding skin model', {elements: Cube.all, exploded_view: exploded_view});
Canvas.updateView({elements: Cube.all, element_aspects: {geometry: true}});
this.setIcon(this.icon);
}
})
Blockbench.on('select_project', () => {
explode_skin_model.value = !!Project.exploded_view;
explode_skin_model.updateEnabledState();
})
})
skin_presets.steve = {

View File

@ -677,3 +677,25 @@ BARS.defineActions(function() {
}
})
})
Interface.definePanels(function() {
new Panel('bone', {
icon: 'fas.fa-bone',
condition: !Blockbench.isMobile && {modes: ['animate']},
selection_only: true,
default_position: {
slot: 'right_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
component: {
template: `
<div>
<p>${ tl('panel.element.origin') }</p>
<div class="toolbar_wrapper bone_origin"></div>
</div>
`
}
})
})

View File

@ -1237,6 +1237,12 @@ Interface.definePanels(function() {
new Panel('outliner', {
icon: 'list_alt',
condition: {modes: ['edit', 'paint', 'animate', 'pose']},
default_position: {
slot: 'right_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
head: Toolbars.outliner
},
@ -1500,6 +1506,24 @@ Interface.definePanels(function() {
if (Modes.edit) Interface.addSuggestedModifierKey('alt', 'modifier_actions.drag_to_duplicate');
}
})
new Panel('element', {
icon: 'fas.fa-cube',
condition: !Blockbench.isMobile && {modes: ['edit', 'pose']},
selection_only: true,
default_position: {
slot: 'right_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
element_position: !Blockbench.isMobile && Toolbars.element_position,
element_size: !Blockbench.isMobile && Toolbars.element_size,
element_origin: !Blockbench.isMobile && Toolbars.element_origin,
element_rotation: !Blockbench.isMobile && Toolbars.element_rotation,
}
})
})
class Face {

View File

@ -60,6 +60,12 @@ Interface.definePanels(() => {
ColorPanel = new Panel('color', {
icon: 'palette',
condition: () => Modes.id === 'paint',
default_position: {
slot: 'right_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
color_picker: Toolbars.color_picker,
palette: Toolbars.palette

View File

@ -1718,6 +1718,12 @@ Interface.definePanels(function() {
icon: 'fas.fa-images',
growable: true,
condition: {modes: ['edit', 'paint']},
default_position: {
slot: 'left_bar',
float_position: [0, 0],
float_size: [300, 400],
height: 400
},
toolbars: {
head: Toolbars.texturelist
},

View File

@ -1671,6 +1671,12 @@ Interface.definePanels(function() {
icon: 'photo_size_select_large',
selection_only: true,
condition: {modes: ['edit', 'paint']},
default_position: {
slot: 'left_bar',
float_position: [300, 0],
float_size: [500, 600],
height: 500
},
toolbars: {
bottom: Toolbars.UVEditor
},

View File

@ -1527,6 +1527,7 @@
"panel.outliner": "Outliner",
"panel.chat": "Chat",
"panel.animations": "Animations",
"panel.timeline": "Timeline",
"panel.keyframe": "Keyframe",
"panel.keyframe.type": "Keyframe (%0)",
"panel.keyframe.toggle_uniform_scale": "Toggle Uniform Scale",