blockbench/js/interface/panels.js

493 lines
17 KiB
JavaScript
Raw Normal View History

2020-07-16 15:32:59 +08:00
class Panel {
2022-02-23 01:12:27 +08:00
constructor(id, data) {
if (!data) data = id;
2020-07-16 15:32:59 +08:00
let scope = this;
this.type = 'panel';
2022-02-23 01:12:27 +08:00
this.id = typeof id == 'string' ? id : data.id || 'new_panel';
this.name = tl(data.name ? data.name : `panel.${this.id}`);
2020-07-16 15:32:59 +08:00
this.icon = data.icon;
this.menu = data.menu;
2022-02-23 01:12:27 +08:00
this.condition = data.condition;
this.previous_slot = 'left_bar';
this.growable = data.growable;
2020-07-16 15:32:59 +08:00
this.selection_only = data.selection_only == true;
2022-02-23 01:12:27 +08:00
this.folded = false;
2020-07-16 15:32:59 +08:00
this.onResize = data.onResize;
this.onFold = data.onFold;
2022-02-23 01:12:27 +08:00
this.toolbars = data.toolbars || {};
2022-03-04 04:13:30 +08:00
if (!Interface.data.panels[this.id]) Interface.data.panels[this.id] = {};
2022-02-23 01:12:27 +08:00
this.position_data = Interface.data.panels[this.id];
2022-03-04 04:13:30 +08:00
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;
2022-02-23 01:12:27 +08:00
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);
if (this.selection_only) this.node.classList.add('selection_only');
if (this.growable) this.node.classList.add('grow');
// Toolbars
for (let key in this.toolbars) {
let toolbar = this.toolbars[key];
if (toolbar instanceof Toolbar) {
if (toolbar.label) {
let label = Interface.createElement('p', {class: 'panel_toolbar_label'}, tl(toolbar.name));
this.node.append(label);
}
this.node.append(toolbar.node);
}
2020-07-16 15:32:59 +08:00
}
2022-02-23 01:12:27 +08:00
2020-10-05 05:44:06 +08:00
if (data.component) {
2022-02-23 01:12:27 +08:00
2022-03-04 04:13:30 +08:00
let component_mount = Interface.createElement('div');
2022-02-23 01:12:27 +08:00
this.node.append(component_mount);
2022-03-04 04:13:30 +08:00
let onmounted = data.component.mounted;
2022-02-23 01:12:27 +08:00
data.component.mounted = function() {
Vue.nextTick(() => {
2020-09-26 18:04:40 +08:00
2022-02-23 01:12:27 +08:00
let toolbar_wrappers = this.$el.querySelectorAll('.toolbar_wrapper');
toolbar_wrappers.forEach(wrapper => {
let id = wrapper.attributes.toolbar && wrapper.attributes.toolbar.value;
let toolbar = scope.toolbars[id];
if (toolbar) {
wrapper.append(toolbar.node);
}
})
2022-03-04 04:13:30 +08:00
if (typeof onmounted == 'function') {
onmounted.call(this);
2022-02-23 01:12:27 +08:00
}
updateInterfacePanels()
})
2020-09-26 18:04:40 +08:00
}
2022-03-04 04:13:30 +08:00
this.vue = this.inside_vue = new Vue(data.component).$mount(component_mount);
scope.vue.$el.classList.add('panel_vue_wrapper');
2020-07-16 15:32:59 +08:00
}
if (!Blockbench.isMobile) {
2022-02-23 01:12:27 +08:00
let snap_button = Interface.createElement('div', {class: 'tool panel_control'}, Blockbench.getIconNode('drag_handle'))
this.handle.append(snap_button);
snap_button.addEventListener('click', (e) => {
new Menu([
{
2022-03-04 04:13:30 +08:00
name: 'Left Sidebar',
icon: 'align_horizontal_left',
click: () => this.moveTo('left_bar')
2022-02-23 01:12:27 +08:00
},
{
2022-03-04 04:13:30 +08:00
name: 'Right Sidebar',
icon: 'align_horizontal_right',
click: () => this.moveTo('right_bar')
2022-02-23 01:12:27 +08:00
},
{
name: 'Top',
2022-03-04 04:13:30 +08:00
icon: 'align_vertical_top',
click: () => this.moveTo('top')
2022-02-23 01:12:27 +08:00
},
{
name: 'Bottom',
2022-03-04 04:13:30 +08:00
icon: 'align_vertical_bottom',
click: () => this.moveTo('bottom')
2022-02-23 01:12:27 +08:00
},
{
name: 'Float',
2022-03-04 04:13:30 +08:00
icon: 'web_asset',
click: () => this.moveTo('float')
2022-02-23 01:12:27 +08:00
}
]).show(snap_button);
})
let fold_button = Interface.createElement('div', {class: 'tool panel_control panel_folding_button'}, Blockbench.getIconNode('expand_more'))
this.handle.append(fold_button);
fold_button.addEventListener('click', (e) => {
this.fold();
})
2022-03-05 06:34:36 +08:00
this.handle.firstElementChild.addEventListener('dblclick', e => {
this.fold();
})
2022-02-23 01:12:27 +08:00
addEventListeners(this.handle.firstElementChild, 'mousedown touchstart', e1 => {
convertTouchEvent(e1);
let started = false;
let position_before = this.slot == 'float'
? this.position_data.float_position.slice()
: [e1.clientX - e1.offsetX, e1.clientY - e1.offsetY - 55];
let drag = e2 => {
convertTouchEvent(e2);
if (!started && (Math.pow(e2.clientX - e1.clientX, 2) + Math.pow(e2.clientX - e1.clientX, 2)) > 15) {
started = true;
2022-03-05 06:34:36 +08:00
if (this.slot !== 'float') {
this.moveTo('float');
this.moveToFront();
}
2022-02-23 01:12:27 +08:00
}
if (!started) return;
2022-03-05 06:34:36 +08:00
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;
2022-02-23 01:12:27 +08:00
2022-03-05 06:34:36 +08:00
let threshold = -8;
if (this.position_data.float_position[0] < threshold) {
let panels = [];
Interface.left_bar.childNodes.forEach(child => {
if (child.clientHeight) {
panels.push(child.id.replace(/^panel_/, ''));
}
})
let anchor = this.position_data.float_position[1];
anchor += this.node.clientHeight * ((this.position_data.float_position[1] + this.position_data.float_size[1]) / Interface.work_screen.clientHeight);
let index = Math.floor(Math.clamp(anchor / Interface.work_screen.clientHeight, 0, 1) * (panels.length+1));
this.moveTo('left_bar', Panels[panels[Math.clamp(index, 0, panels.length-1)]], index < panels.length);
} else if (this.position_data.float_position[0] + Math.min(this.position_data.float_size[0], Interface.data.right_bar_width) > document.body.clientWidth - threshold) {
let panels = [];
Interface.right_bar.childNodes.forEach(child => {
if (child.clientHeight) {
panels.push(child.id.replace(/^panel_/, ''));
}
})
let anchor = this.position_data.float_position[1];
anchor += this.node.clientHeight * ((this.position_data.float_position[1] + this.position_data.float_size[1]) / Interface.work_screen.clientHeight);
let index = Math.floor(Math.clamp(anchor / Interface.work_screen.clientHeight, 0, 1) * (panels.length+1));
this.moveTo('right_bar', Panels[panels[Math.clamp(index, 0, panels.length-1)]], index < panels.length);
} else if (this.position_data.float_position[1] < threshold) {
if (this.slot == 'float') this.moveTo('top');
} else if (this.position_data.float_position[1] + Math.min(this.position_data.float_size[1], 200) > Interface.work_screen.clientHeight - threshold) {
if (this.slot == 'float') this.moveTo('bottom');
} else if (this.slot != 'float') {
2022-03-04 04:13:30 +08:00
this.moveTo('float');
}
2022-03-05 06:34:36 +08:00
this.update(true);
2022-02-23 01:12:27 +08:00
}
let stop = e2 => {
convertTouchEvent(e2);
2022-03-05 06:34:36 +08:00
if (this.slot != 'float') {
this.position_data.float_position[0] = position_before[0];
this.position_data.float_position[1] = position_before[1];
}
this.update();
2022-02-23 01:12:27 +08:00
saveSidebarOrder()
updateInterface()
removeEventListeners(document, 'mousemove touchmove', drag);
removeEventListeners(document, 'mouseup touchend', stop);
}
addEventListeners(document, 'mousemove touchmove', drag);
addEventListeners(document, 'mouseup touchend', stop);
})
} else {
let fold_button = Interface.createElement('div', {class: 'tool panel_control panel_folding_button'}, Blockbench.getIconNode('expand_more'))
this.handle.append(fold_button);
fold_button.addEventListener('click', (e) => {
this.fold();
2020-07-16 15:32:59 +08:00
})
2022-02-23 01:12:27 +08:00
let close_button = Interface.createElement('div', {class: 'tool panel_control'}, Blockbench.getIconNode('clear'))
this.handle.append(close_button);
close_button.addEventListener('click', (e) => {
Interface.PanelSelectorVue.select(null);
2020-07-16 15:32:59 +08:00
})
2022-02-23 01:12:27 +08:00
}
this.node.addEventListener('mousedown', event => {
2022-03-04 04:13:30 +08:00
setActivePanel(this.id);
this.moveToFront();
2022-02-23 01:12:27 +08:00
})
2022-03-04 04:13:30 +08:00
// Add to slot
2022-02-23 01:12:27 +08:00
let reference_panel = Panels[data.insert_before || data.insert_after];
this.moveTo(this.position_data.slot, reference_panel, reference_panel && !data.insert_after);
2020-07-16 15:32:59 +08:00
Interface.Panels[this.id] = this;
}
isVisible() {
return !this.folded && this.node.parentElement && this.node.parentElement.style.display !== 'none';
}
2022-02-23 01:12:27 +08:00
get slot() {
return this.position_data.slot;
}
fold(state = !this.folded) {
this.folded = !!state;
2022-02-23 01:12:27 +08:00
let new_icon = Blockbench.getIconNode(state ? 'expand_less' : 'expand_more');
$(this.handle).find('> .panel_folding_button > .icon').replaceWith(new_icon);
this.node.classList.toggle('folded', state);
if (this.onFold) {
this.onFold();
}
2022-03-04 04:13:30 +08:00
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;
}
2022-02-23 01:12:27 +08:00
moveTo(slot, ref_panel, before = false) {
let position_data = this.position_data;
if (slot == undefined) {
slot = ref_panel.position_data.slot;
}
this.node.classList.remove('floating');
2022-03-04 04:13:30 +08:00
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;
}
}
2022-02-23 01:12:27 +08:00
if (ref_panel instanceof Panel && ref_panel.slot == slot) {
if (before) {
$(ref_panel.node).before(this.node);
} else {
$(ref_panel.node).after(this.node);
}
2020-07-16 15:32:59 +08:00
} else {
2022-03-04 04:13:30 +08:00
document.getElementById(slot).append(this.node);
2020-07-16 15:32:59 +08:00
}
2022-02-23 01:12:27 +08:00
} else if (slot == 'top') {
let top_panel = Interface.getTopPanel();
if (top_panel && top_panel !== this) top_panel.moveTo(top_panel.previous_slot);
document.getElementById('top_slot').append(this.node);
} else if (slot == 'bottom') {
let bottom_panel = Interface.getBottomPanel();
if (bottom_panel && bottom_panel !== this) bottom_panel.moveTo(bottom_panel.previous_slot);
document.getElementById('bottom_slot').append(this.node);
} else if (slot == 'float') {
2022-03-05 06:34:36 +08:00
Interface.work_screen.append(this.node);
2022-02-23 01:12:27 +08:00
this.node.classList.add('floating');
2022-03-04 04:13:30 +08:00
if (!this.resize_handles) {
this.setupFloatHandles();
}
2020-07-16 15:32:59 +08:00
}
2022-02-23 01:12:27 +08:00
position_data.slot = slot;
2022-03-04 04:13:30 +08:00
this.update();
2022-02-23 01:12:27 +08:00
if (Panels[this.id]) {
// Only update after initial setup
if (this.onResize) {
this.onResize()
}
saveSidebarOrder()
updateInterface()
2020-07-16 15:32:59 +08:00
}
2022-03-04 04:13:30 +08:00
return this;
2020-07-16 15:32:59 +08:00
}
2022-03-05 06:34:36 +08:00
update(dragging) {
2022-02-23 01:12:27 +08:00
let show = BARS.condition(this.condition);
2022-03-04 04:13:30 +08:00
let work_screen = document.querySelector('div#work_screen');
2022-02-23 01:12:27 +08:00
let center_screen = document.querySelector('div#center');
2020-07-16 15:32:59 +08:00
if (show) {
$(this.node).show()
2022-03-04 04:13:30 +08:00
if (this.slot == 'float') {
2022-03-05 06:34:36 +08:00
if (!dragging) {
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]);
}
2022-03-04 04:13:30 +08:00
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;
2022-02-23 01:12:27 +08:00
} else if (this.slot == 'left_bar') {
2020-07-16 15:32:59 +08:00
this.width = Interface.data.left_bar_width
2022-02-23 01:12:27 +08:00
} else if (this.slot == 'right_bar') {
2020-07-16 15:32:59 +08:00
this.width = Interface.data.right_bar_width
}
2022-02-23 01:12:27 +08:00
if (this.slot == 'top' || this.slot == 'bottom') {
this.height = Math.clamp(this.position_data.height, 30, center_screen.clientHeight);
2022-03-04 04:13:30 +08:00
if (this.folded) this.height = this.handle.clientHeight;
2022-02-23 01:12:27 +08:00
this.node.style.height = this.height + 'px';
}
2022-03-04 04:13:30 +08:00
if (Panels[this.id] && this.onResize) this.onResize()
2020-07-16 15:32:59 +08:00
} else {
$(this.node).hide()
}
2022-02-23 01:12:27 +08:00
localStorage.setItem('interface_data', JSON.stringify(Interface.data))
2022-03-04 04:13:30 +08:00
return this;
2020-07-16 15:32:59 +08:00
}
delete() {
delete Interface.Panels[this.id];
2020-11-28 06:23:16 +08:00
this.node.remove()
2020-07-16 15:32:59 +08:00
}
}
2022-03-04 04:13:30 +08:00
Panel.floating_panel_z_order = [];
2020-07-16 15:32:59 +08:00
function setupPanels() {
Interface.panel_definers.forEach((definer) => {
if (typeof definer === 'function') {
definer()
2020-07-16 15:32:59 +08:00
}
})
2022-03-04 04:13:30 +08:00
updateSidebarOrder();
2022-02-23 01:12:27 +08:00
}
2020-07-16 15:32:59 +08:00
2022-02-23 01:12:27 +08:00
function updateInterfacePanels() {
2020-07-16 15:32:59 +08:00
2022-02-23 01:12:27 +08:00
if (!Blockbench.isMobile) {
$('.sidebar#left_bar').css('display', Prop.show_left_bar ? 'flex' : 'none');
$('.sidebar#right_bar').css('display', Prop.show_right_bar ? 'flex' : 'none');
}
2022-03-05 06:34:36 +08:00
let work_screen = Interface.work_screen;
2020-07-16 15:32:59 +08:00
2022-02-23 01:12:27 +08:00
work_screen.style.setProperty(
'grid-template-columns',
Interface.data.left_bar_width+'px auto '+ Interface.data.right_bar_width +'px'
)
for (var key in Interface.Panels) {
var panel = Interface.Panels[key]
panel.update()
}
var left_width = $('.sidebar#left_bar > .panel:visible').length ? Interface.left_bar_width : 0;
var right_width = $('.sidebar#right_bar > .panel:visible').length ? Interface.right_bar_width : 0;
2020-07-16 15:32:59 +08:00
2022-02-23 01:12:27 +08:00
if (!left_width || !right_width) {
work_screen.style.setProperty(
'grid-template-columns',
left_width+'px auto '+ right_width +'px'
)
}
$('.quad_canvas_wrapper.qcw_x').css('width', Interface.data.quad_view_x+'%')
$('.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)+'%')
2022-03-04 04:13:30 +08:00
//$('#timeline').css('height', Interface.data.timeline_height+'px')
2022-02-23 01:12:27 +08:00
for (var key in Interface.Resizers) {
var resizer = Interface.Resizers[key]
resizer.update()
}
}
2020-07-16 15:32:59 +08:00
2022-03-04 04:13:30 +08:00
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];
2022-03-05 06:34:36 +08:00
if (panel) bar_node.append(panel.node);
2022-03-04 04:13:30 +08:00
});
})
}
2020-09-22 05:23:42 +08:00
2020-07-16 15:32:59 +08:00
function setActivePanel(panel) {
Prop.active_panel = panel
}
2022-02-23 01:12:27 +08:00
function saveSidebarOrder() {
2020-07-16 15:32:59 +08:00
localStorage.setItem('interface_data', JSON.stringify(Interface.data))
}