blockbench/js/interface/menu_bar.js
2023-06-17 23:41:07 +02:00

532 lines
14 KiB
JavaScript

class BarMenu extends Menu {
constructor(id, structure, options = {}) {
super(id, structure, options)
var scope = this;
MenuBar.menus[id] = this
this.type = 'bar_menu'
this.id = id
this.children = [];
this.condition = options.condition
this.node = document.createElement('ul');
this.node.className = 'contextMenu';
this.node.style.minHeight = '8px';
this.node.style.minWidth = '150px';
this.name = tl(options.name || `menu.${id}`);
this.label = Interface.createElement('li', {class: 'menu_bar_point'}, this.name);
this.label.addEventListener('click', (event) => {
if (open_menu === scope) {
scope.hide()
} else {
scope.open()
}
})
this.label.addEventListener('mouseenter', (event) => {
if (MenuBar.open && MenuBar.open !== scope) {
scope.open()
}
})
this.structure = structure;
this.highlight_action = null;
}
hide() {
super.hide();
$(this.label).removeClass('opened');
MenuBar.open = undefined;
this.highlight_action = null;
this.label.classList.remove('highlighted');
return this;
}
highlight(action) {
this.highlight_action = action;
this.label.classList.add('highlighted');
}
}
const MenuBar = {
menus: {},
open: undefined,
setup() {
MenuBar.menues = MenuBar.menus;
new BarMenu('file', [
'project_window',
'_',
{name: 'menu.file.new', id: 'new', icon: 'insert_drive_file',
children: function() {
let arr = [];
let redact = settings.streamer_mode.value;
for (let key in Formats) {
let format = Formats[key];
if (!format.show_in_new_list) continue;
arr.push({
id: format.id,
name: (redact && format.confidential) ? `[${tl('generic.redacted')}]` : format.name,
icon: format.icon,
description: format.description,
click: (e) => {
format.new()
}
})
}
arr.push('_');
for (let key in ModelLoader.loaders) {
let loader = ModelLoader.loaders[key];
arr.push({
id: loader.id,
name: (redact && loader.confidential) ? `[${tl('generic.redacted')}]` : loader.name,
icon: loader.icon,
description: loader.description,
click: (e) => {
loader.new()
}
})
}
return arr;
}
},
{name: 'menu.file.recent', id: 'recent', icon: 'history',
condition() {return isApp && recent_projects.length},
searchable: true,
children() {
var arr = []
let redact = settings.streamer_mode.value;
for (let p of recent_projects) {
if (arr.length > 12) break;
arr.push({
name: redact ? `[${tl('generic.redacted')}]` : p.name,
path: p.path,
description: redact ? '' : p.path,
icon: p.icon,
click(c, event) {
Blockbench.read([p.path], {}, files => {
loadModelFile(files[0]);
})
}
})
}
if (recent_projects.length > 12) {
arr.push('_', {
name: 'menu.file.recent.more',
icon: 'read_more',
always_show: true,
click(c, event) {
ActionControl.select('recent: ');
}
})
}
if (arr.length) {
arr.push('_', {
name: 'menu.file.recent.clear',
icon: 'clear',
always_show: true,
click(c, event) {
recent_projects.empty();
updateRecentProjects();
}
})
}
return arr
}
},
'open_model',
'open_from_link',
'new_window',
'_',
'save_project',
'save_project_as',
'convert_project',
'close_project',
'_',
{name: 'menu.file.import', id: 'import', icon: 'insert_drive_file', condition: () => Format && !Format.pose_mode, children: [
{
id: 'import_open_project',
name: 'menu.file.import.import_open_project',
icon: 'input',
condition: () => Project && ModelProject.all.length > 1,
children() {
let projects = [];
ModelProject.all.forEach(project => {
if (project == Project) return;
projects.push({
name: project.getDisplayName(),
icon: project.format.icon,
description: project.path,
click() {
let current_project = Project;
project.select();
let bbmodel = Codecs.project.compile();
current_project.select();
Codecs.project.merge(JSON.parse(bbmodel));
}
})
})
return projects;
}
},
'import_project',
'import_java_block_model',
'import_optifine_part',
'import_obj',
'extrude_texture'
]},
{name: 'generic.export', id: 'export', icon: 'insert_drive_file', condition: () => Project, children: [
'export_blockmodel',
'export_bedrock',
'export_entity',
'export_class_entity',
'export_optifine_full',
'export_optifine_part',
'export_minecraft_skin',
'export_gltf',
'export_obj',
'export_fbx',
'export_collada',
'upload_sketchfab',
'share_model',
]},
'export_over',
'export_asset_archive',
'_',
{name: 'menu.file.preferences', id: 'preferences', icon: 'tune', children: [
'settings_window',
'keybindings_window',
'theme_window',
{
id: 'profiles',
name: 'data.settings_profile',
icon: 'settings_applications',
condition: () => SettingsProfile.all.findIndex(p => p.condition.type == 'selectable') != -1,
children: () => {
let list = [
{
name: 'generic.none',
icon: SettingsProfile.selected ? 'radio_button_unchecked' : 'radio_button_checked',
click: () => {
SettingsProfile.unselect();
}
},
'_'
];
SettingsProfile.all.forEach(profile => {
if (profile.condition.type != 'selectable') return;
list.push({
name: profile.name,
icon: profile.selected ? 'radio_button_checked' : 'radio_button_unchecked',
color: markerColors[profile.color].standard,
click: () => {
profile.select();
}
})
})
return list;
}
}
]},
'plugins_window',
'edit_session'
])
new BarMenu('edit', [
'undo',
'redo',
'edit_history',
'_',
'add_cube',
'add_mesh',
'add_group',
'add_locator',
'add_null_object',
'add_texture_mesh',
'_',
'duplicate',
'rename',
'find_replace',
'unlock_everything',
'delete',
'_',
{name: 'data.mesh', id: 'mesh', icon: 'fa-gem', children: [
'extrude_mesh_selection',
'inset_mesh_selection',
'loop_cut',
'create_face',
'invert_face',
'switch_face_crease',
'merge_vertices',
'dissolve_edges',
'split_mesh',
'merge_meshes',
'_',
'proportional_editing',
]},
'_',
'select_window',
'select_all',
'unselect_all',
'invert_selection'
])
new BarMenu('transform', [
'scale',
{name: 'menu.transform.rotate', id: 'rotate', icon: 'rotate_90_degrees_ccw', children: [
'rotate_x_cw',
'rotate_x_ccw',
'rotate_y_cw',
'rotate_y_ccw',
'rotate_z_cw',
'rotate_z_ccw'
]},
{name: 'menu.transform.flip', id: 'flip', icon: 'flip', children: [
'flip_x',
'flip_y',
'flip_z'
]},
{name: 'menu.transform.center', id: 'center', icon: 'filter_center_focus', children: [
'center_x',
'center_y',
'center_z',
'center_lateral'
]},
{name: 'menu.transform.properties', id: 'properties', icon: 'navigate_next', children: [
'toggle_visibility',
'toggle_locked',
'toggle_export',
'toggle_autouv',
'toggle_shade',
'toggle_mirror_uv'
]}
], {
condition: {modes: ['edit']}
})
new BarMenu('uv', UVEditor.menu.structure, {
condition: {modes: ['edit']},
onOpen() {
setActivePanel('uv');
}
})
new BarMenu('texture', [
'adjust_brightness_contrast',
'adjust_saturation_hue',
'adjust_opacity',
'invert_colors',
'adjust_curves',
'_',
'limit_to_palette',
'clear_unused_texture_space',
'_',
'flip_texture_x',
'flip_texture_y',
'rotate_texture_cw',
'rotate_texture_ccw',
'resize_texture'
], {
condition: {modes: ['paint']}
})
new BarMenu('animation', [
'looped_animation_playback',
'lock_motion_trail',
'_',
'add_marker',
'select_effect_animator',
'flip_animation',
'bake_animation_into_model',
'_',
'load_animation_file',
'save_all_animations',
'export_animation_file'
], {
condition: {modes: ['animate']}
})
new BarMenu('keyframe', [
'copy',
'paste',
'_',
'add_keyframe',
'keyframe_column_create',
'select_all',
'keyframe_column_select',
'reverse_keyframes',
{name: 'menu.animation.flip_keyframes', id: 'flip_keyframes', condition: () => Timeline.selected.length, icon: 'flip', children: [
'flip_x',
'flip_y',
'flip_z'
]},
'keyframe_uniform',
'reset_keyframe',
'resolve_keyframe_expressions',
'delete',
], {
condition: {modes: ['animate']}
})
new BarMenu('timeline', Timeline.menu.structure, {
name: 'panel.timeline',
condition: {modes: ['animate'], method: () => !AnimationController.selected},
onOpen() {
setActivePanel('timeline');
}
})
new BarMenu('display', [
'copy',
'paste',
'_',
'add_display_preset',
'apply_display_preset'
], {
condition: {modes: ['display']}
})
new BarMenu('tools', [
{id: 'main_tools', icon: 'construction', name: 'Toolbox', condition: () => Project, children() {
let tools = Toolbox.children.filter(tool => tool instanceof Tool && tool.condition !== false);
tools.forEach(tool => {
let old_condition = tool.condition;
tool.condition = () => {
tool.condition = old_condition;
return true;
}
})
let modes = Object.keys(Modes.options);
tools.sort((a, b) => modes.indexOf(a.modes[0]) - modes.indexOf(b.modes[0]))
let mode = tools[0].modes[0];
for (let i = 0; i < tools.length; i++) {
if (tools[i].modes[0] !== mode) {
mode = tools[i].modes[0];
tools.splice(i, 0, '_');
i++;
}
}
return tools;
}},
'swap_tools',
'action_control',
'_',
'predicate_overrides',
'convert_to_mesh',
'auto_set_cullfaces',
'remove_blank_faces',
])
MenuBar.menus.filter = MenuBar.menus.tools;
new BarMenu('view', [
'fullscreen',
'_',
'view_mode',
'toggle_shading',
'toggle_motion_trails',
'toggle_ground_plane',
'preview_checkerboard',
'painting_grid',
'_',
'preview_scene',
'edit_reference_images',
'_',
'toggle_sidebars',
'split_screen',
'hide_everything_except_selection',
'focus_on_selection',
{name: 'menu.view.screenshot', id: 'screenshot', icon: 'camera_alt', children: []},
'_',
'screenshot_model',
'screenshot_app',
'record_model_gif',
'timelapse',
])
new BarMenu('help', [
{name: 'menu.help.search_action', description: BarItems.action_control.description, keybind: BarItems.action_control.keybind, id: 'search_action', icon: 'search', click: ActionControl.select},
'_',
{name: 'menu.help.quickstart', id: 'quickstart', icon: 'fas.fa-directions', click: () => {
Blockbench.openLink('https://blockbench.net/quickstart/');
}},
{name: 'menu.help.discord', id: 'discord', icon: 'fab.fa-discord', click: () => {
Blockbench.openLink('http://discord.blockbench.net');
}},
{name: 'menu.help.wiki', id: 'wiki', icon: 'menu_book', click: () => {
Blockbench.openLink('https://blockbench.net/wiki/');
}},
{name: 'menu.help.report_issue', id: 'report_issue', icon: 'bug_report', click: () => {
Blockbench.openLink('https://github.com/JannisX11/blockbench/issues');
}},
'_',
'view_backups',
'_',
{name: 'menu.help.developer', id: 'developer', icon: 'fas.fa-wrench', children: [
'reload_plugins',
{name: 'menu.help.plugin_documentation', id: 'plugin_documentation', icon: 'fa-book', click: () => {
Blockbench.openLink('https://www.blockbench.net/wiki/docs/plugin');
}},
'open_dev_tools',
{name: 'Error Log', condition: () => window.ErrorLog.length, icon: 'error', color: 'red', keybind: {toString: () => window.ErrorLog.length.toString()}, click() {
let lines = window.ErrorLog.slice(0, 64).map((error) => {
return Interface.createElement('p', {}, `${error.message}\n - In .${error.file.split(location.origin).join('')} : ${error.line}`);
})
new Dialog({
id: 'error_log',
title: 'Error Log',
lines,
singleButton: true
}).show();
}},
'reset_layout',
{name: 'menu.help.developer.reset_storage', icon: 'fas.fa-hdd', click: () => {
if (confirm(tl('menu.help.developer.reset_storage.confirm'))) {
localStorage.clear()
Blockbench.addFlag('no_localstorage_saving')
console.log('Cleared Local Storage')
window.location.reload(true)
}
}},
{name: 'menu.help.developer.unlock_projects', id: 'unlock_projects', icon: 'vpn_key', condition: () => ModelProject.all.find(project => project.locked), click() {
ModelProject.all.forEach(project => project.locked = false);
}},
{name: 'menu.help.developer.cache_reload', id: 'cache_reload', icon: 'cached', condition: !isApp, click: () => {
if('caches' in window){
caches.keys().then((names) => {
names.forEach(async (name) => {
await caches.delete(name)
})
})
}
window.location.reload(true)
}},
'reload',
]},
'about_window'
])
MenuBar.update()
},
update() {
var bar = $('#menu_bar')
bar.children().detach()
this.keys = []
for (var menu in MenuBar.menus) {
if (MenuBar.menus.hasOwnProperty(menu)) {
if (MenuBar.menus[menu].conditionMet()) {
bar.append(MenuBar.menus[menu].label)
this.keys.push(menu)
}
}
}
},
addAction(action, path) {
if (path) {
path = path.split('.')
var menu = MenuBar.menus[path.splice(0, 1)[0]]
if (menu) {
menu.addAction(action, path.join('.'))
}
}
},
removeAction(path) {
if (path) {
path = path.split('.')
var menu = MenuBar.menus[path.splice(0, 1)[0]]
if (menu) {
menu.removeAction(path.join('.'))
}
}
}
}