Fix tab selection issues

Fix issue with save dialog when closing program
UV Editor no longer updates when hidden, improving performance
Fix animated texture preview in UV panel
This commit is contained in:
JannisX11 2021-09-15 17:50:46 +02:00
parent 35767f018f
commit 1b5bb9aba8
12 changed files with 145 additions and 114 deletions

View File

@ -1,5 +1,5 @@
const electron = require('electron').remote;
const {clipboard, shell, nativeImage, ipcRenderer} = require('electron');
const {clipboard, shell, nativeImage, ipcRenderer, dialog} = require('electron');
const app = electron.app;
const fs = require('fs');
const NodeBuffer = require('buffer');
@ -29,32 +29,6 @@ const recent_projects = (function() {
app.setAppUserModelId('blockbench')
// Deprecated
const ElecDialogs = {};
if (electron.dialog.showMessageBoxSync) {
ElecDialogs.showMessageBox = function(a, b, cb) {
if (!cb) cb = b;
var result = electron.dialog.showMessageBoxSync(a, b);
if (typeof cb == 'function') cb(result);
return result;
}
ElecDialogs.showSaveDialog = function(a, b, cb) {
if (!cb) cb = b;
var result = electron.dialog.showSaveDialogSync(a, b);
if (typeof cb == 'function') cb(result);
return result;
}
ElecDialogs.showOpenDialog = function(a, b, cb) {
if (!cb) cb = b;
var result = electron.dialog.showOpenDialogSync(a, b);
if (typeof cb == 'function') cb(result);
return result;
}
} else {
ElecDialogs.showMessageBox = electron.dialog.showMessageBox;
ElecDialogs.showSaveDialog = electron.dialog.showSaveDialog;
ElecDialogs.showOpenDialog = electron.dialog.showOpenDialog;
}
function initializeDesktopApp() {
@ -275,17 +249,16 @@ function changeImageEditor(texture, from_settings) {
}).show()
}
function selectImageEditorFile(texture) {
ElecDialogs.showOpenDialog(currentwindow, {
let filePaths = electron.dialog.showOpenDialogSync(currentwindow, {
title: tl('message.image_editor.exe'),
filters: [{name: 'Executable Program', extensions: ['exe', 'app']}]
}, function(filePaths) {
if (filePaths) {
settings.image_editor.value = filePaths[0]
if (texture) {
texture.openEditor()
}
}
})
if (filePaths) {
settings.image_editor.value = filePaths[0]
if (texture) {
texture.openEditor();
}
}
}
//Default Pack
function openDefaultTexturePath() {
@ -388,8 +361,18 @@ window.onbeforeunload = function (event) {
}
} catch (err) {}
} else {
setTimeout(function() {
showSaveDialog(true)
setTimeout(async function() {
let projects = ModelProject.all.slice();
for (let project of projects) {
let closed = await project.close();
if (!closed) return false;
}
if (ModelProject.all.length === 0) {
closeBlockbenchWindow()
return true;
} else {
return false;
}
}, 2)
event.returnValue = true;
return true;
@ -397,16 +380,18 @@ window.onbeforeunload = function (event) {
}
function showSaveDialog(close) {
/*
if (Blockbench.hasFlag('allow_reload')) {
close = false
}
var unsaved_textures = 0;
Texture.all.forEach(function(t) {
if (!t.saved) {
unsaved_textures++;
}
})
if ((window.Prop && Project.saved === false && (elements.length > 0 || Group.all.length > 0)) || unsaved_textures) {
if (close) {
unsaved_projects = ModelProject.all.find(project => {
return !project.saved || project.textures.find(tex => !tex.saved)
})
}
if (unsaved_changes || !close) {
var answer = electron.dialog.showMessageBoxSync(currentwindow, {
type: 'question',
buttons: [tl('dialog.save'), tl('dialog.discard'), tl('dialog.cancel')],
@ -433,7 +418,7 @@ function showSaveDialog(close) {
closeBlockbenchWindow()
}
return true;
}
}*/
}
function closeBlockbenchWindow() {
window.onbeforeunload = null;

View File

@ -10,6 +10,7 @@ class Panel {
this.selection_only = data.selection_only == true;
this.condition = data.condition;
this.onResize = data.onResize;
this.onFold = data.onFold;
this.folded = false;
if (data.toolbars) {
this.toolbars = data.toolbars;
@ -156,10 +157,16 @@ class Panel {
Interface.Panels[this.id] = this;
}
isVisible() {
return !this.folded && this.node.parentElement && this.node.parentElement.style.display !== 'none';
}
fold(state = !this.folded) {
this.folded = !!state;
$(this.handle).find('> .panel_folding_button > i').text(state ? 'expand_less' : 'expand_more');
this.node.classList.toggle('folded', state);
if (this.onFold) {
this.onFold();
}
}
moveTo(ref_panel, before) {
let scope = this

View File

@ -165,7 +165,7 @@ class ModelProject {
}
}
select() {
if (this.selected) return;
if (this === Project) return;
if (Project) {
Project.unselect()
} else {
@ -269,8 +269,34 @@ class ModelProject {
Blockbench.dispatchEvent('unselect_project', {project: this});
}
async close(force) {
let last_selected = Project;
this.select();
await new Promise(resolve => setTimeout(resolve, 50));
if (force || showSaveDialog()) {
function saveWarning() {
if (Project.saved && !Project.textures.find(tex => !tex.saved)) {
return true;
} else {
if (isApp) {
var answer = electron.dialog.showMessageBoxSync(currentwindow, {
type: 'question',
buttons: [tl('dialog.save'), tl('dialog.discard'), tl('dialog.cancel')],
title: 'Blockbench',
message: tl('message.close_warning.message'),
noLink: true
})
if (answer === 0) {
BarItems.save_project.trigger();
}
return answer !== 2;
} else {
var answer = confirm(tl('message.close_warning.web'))
return answer;
}
}
}
if (force || saveWarning()) {
if (isApp) await updateRecentProjectThumbnail();
Blockbench.dispatchEvent('close_project');
@ -278,12 +304,15 @@ class ModelProject {
this.unselect();
Texture.all.forEach(tex => tex.stopWatcher());
let index = ModelProject.all.indexOf(this);
ModelProject.all.remove(this);
delete ProjectData[this.uuid];
Project = 0;
if (ModelProject.all.length) {
ModelProject.all[0].select();
if (last_selected !== this) {
last_selected.select();
} else if (ModelProject.all.length) {
ModelProject.all[Math.clamp(index, 0, ModelProject.all.length-1)].select();
} else {
Interface.tab_bar.new_tab.visible = true;
Interface.tab_bar.new_tab.select();
@ -451,8 +480,8 @@ onVueSetup(() => {
close: () => {
if (ModelProject.all.length) {
Interface.tab_bar.new_tab.visible = false;
let project = Project.all.find(project => project.uuid == Interface.tab_bar.last_opened_project) ||
Project.all.last();
let project = ModelProject.all.find(project => project.uuid == Interface.tab_bar.last_opened_project) ||
ModelProject.all.last();
if (project) project.select();
} else {
window.close();

View File

@ -442,13 +442,13 @@ class Cube extends OutlinerElement {
}
transferOrigin(origin, update = true) {
if (!this.mesh) return;
var q = new THREE.Quaternion().copy(this.mesh.quaternion)
var shift = new THREE.Vector3(
var q = Reusable.quat1.copy(this.mesh.quaternion)
var shift = Reusable.vec1.set(
this.origin[0] - origin[0],
this.origin[1] - origin[1],
this.origin[2] - origin[2],
)
var dq = new THREE.Vector3().copy(shift)
var dq = Reusable.vec2.copy(shift)
dq.applyQuaternion(q)
shift.sub(dq)
shift.applyQuaternion(q.invert())
@ -463,7 +463,7 @@ class Cube extends OutlinerElement {
}
getWorldCenter() {
var m = this.mesh;
var pos = new THREE.Vector3(
var pos = Reusable.vec1.set(
this.from[0] + this.size(0)/2,
this.from[1] + this.size(1)/2,
this.from[2] + this.size(2)/2
@ -473,9 +473,9 @@ class Cube extends OutlinerElement {
pos.z -= this.origin[2]
if (m) {
var r = m.getWorldQuaternion(new THREE.Quaternion())
var r = m.getWorldQuaternion(Reusable.quat1)
pos.applyQuaternion(r)
pos.add(THREE.fastWorldPosition(m, new THREE.Vector3()))
pos.add(THREE.fastWorldPosition(m, Reusable.vec2))
}
return pos;
}

View File

@ -64,15 +64,15 @@ class Locator extends OutlinerElement {
return this;
}
getWorldCenter() {
var pos = new THREE.Vector3();
var q = new THREE.Quaternion();
var pos = Reusable.vec1.set(0, 0, 0);
var q = Reusable.quat1.set(0, 0, 0, 1);
if (this.parent instanceof Group) {
THREE.fastWorldPosition(this.parent.mesh, pos);
this.parent.mesh.getWorldQuaternion(q);
var offset2 = new THREE.Vector3().fromArray(this.parent.origin).applyQuaternion(q);
var offset2 = Reusable.vec2.fromArray(this.parent.origin).applyQuaternion(q);
pos.sub(offset2);
}
var offset = new THREE.Vector3().fromArray(this.from).applyQuaternion(q);
var offset = Reusable.vec3.fromArray(this.from).applyQuaternion(q);
pos.add(offset);
return pos;

View File

@ -159,7 +159,7 @@ class Mesh extends OutlinerElement {
}
getWorldCenter(ignore_selected_vertices) {
let m = this.mesh;
let pos = new THREE.Vector3()
let pos = Reusable.vec1.set(0, 0, 0);
let vertice_count = 0;
for (let key in this.vertices) {
@ -176,9 +176,9 @@ class Mesh extends OutlinerElement {
pos.z /= vertice_count;
if (m) {
let r = m.getWorldQuaternion(new THREE.Quaternion());
let r = m.getWorldQuaternion(Reusable.quat1);
pos.applyQuaternion(r);
pos.add(THREE.fastWorldPosition(m, new THREE.Vector3()));
pos.add(THREE.fastWorldPosition(m, Reusable.vec2));
}
return pos;
}
@ -704,21 +704,25 @@ new NodePreviewController(Mesh, {
},
updateSelection(element) {
NodePreviewController.prototype.updateSelection(element);
let mesh = element.mesh;
let colors = [];
let line_colors = [];
for (let key in element.vertices) {
let color;
if (Project.selected_vertices[element.uuid] && Project.selected_vertices[element.uuid].includes(key)) {
color = gizmo_colors.outline;
} else {
color = gizmo_colors.grid;
if (BarItems.selection_mode.value == 'vertex') {
let colors = [];
for (let key in element.vertices) {
let color;
if (Project.selected_vertices[element.uuid] && Project.selected_vertices[element.uuid].includes(key)) {
color = gizmo_colors.outline;
} else {
color = gizmo_colors.grid;
}
colors.push(color.r, color.g, color.b);
}
colors.push(color.r, color.g, color.b);
mesh.vertex_points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
mesh.outline.geometry.needsUpdate = true;
}
let line_colors = [];
mesh.outline.vertex_order.forEach(key => {
let color;
if (!Modes.edit || BarItems.selection_mode.value == 'object' || (Project.selected_vertices[element.uuid] && Project.selected_vertices[element.uuid].includes(key))) {
@ -728,10 +732,9 @@ new NodePreviewController(Mesh, {
}
line_colors.push(color.r, color.g, color.b);
})
mesh.vertex_points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
mesh.outline.geometry.setAttribute('color', new THREE.Float32BufferAttribute(line_colors, 3));
mesh.outline.geometry.needsUpdate = true
mesh.outline.geometry.needsUpdate = true;
mesh.vertex_points.visible = Mode.selected.id == 'edit' && BarItems.selection_mode.value == 'vertex';
},
updateHighlight(element, hover_cube, force_off) {
@ -1664,7 +1667,7 @@ BARS.defineActions(function() {
vector2.fromArray(f.vertex_normals[0]);
let angle = vector1.angleTo(vector2);
if (angle > Math.PI/2) {
new_face.invert();
face.invert();
}
}
}

View File

@ -60,15 +60,15 @@ class NullObject extends OutlinerElement {
return this;
}
getWorldCenter() {
var pos = new THREE.Vector3();
var q = new THREE.Quaternion();
var pos = Reusable.vec1.set(0, 0, 0);
var q = Reusable.quat1.set(0, 0, 0, 1);
if (this.parent instanceof Group) {
THREE.fastWorldPosition(this.parent.mesh, pos);
this.parent.mesh.getWorldQuaternion(q);
var offset2 = new THREE.Vector3().fromArray(this.parent.origin).applyQuaternion(q);
var offset2 = Reusable.vec2.fromArray(this.parent.origin).applyQuaternion(q);
pos.sub(offset2);
}
var offset = new THREE.Vector3().fromArray(this.from).applyQuaternion(q);
var offset = Reusable.vec3.fromArray(this.from).applyQuaternion(q);
pos.add(offset);
return pos;

View File

@ -465,11 +465,11 @@ class OutlinerElement extends OutlinerNode {
s.selected = false;
})
Blockbench.dispatchEvent('added_to_selection', {added: just_selected})
updateSelection()
TickUpdates.selection = true;
return this;
}
selectLow() {
selected.safePush(this);
Outliner.selected.safePush(this);
this.constructor.selected.safePush(this)
this.selected = true;
TickUpdates.selection = true;

View File

@ -14,12 +14,12 @@ class TextureMesh extends OutlinerElement {
}
getWorldCenter() {
let m = this.mesh;
let pos = new THREE.Vector3().fromArray(this.local_pivot);
let pos = Reusable.vec1.fromArray(this.local_pivot);
if (m) {
let r = m.getWorldQuaternion(new THREE.Quaternion());
let r = m.getWorldQuaternion(Reusable.quat1);
pos.applyQuaternion(r);
pos.add(THREE.fastWorldPosition(m, new THREE.Vector3()));
pos.add(THREE.fastWorldPosition(m, Reusable.vec2));
}
return pos;
}

View File

@ -18,6 +18,21 @@ function getRescalingFactor(angle) {
break;
}
}
const Reusable = {
vec1: new THREE.Vector3(),
vec2: new THREE.Vector3(),
vec3: new THREE.Vector3(),
vec4: new THREE.Vector3(),
vec5: new THREE.Vector3(),
vec6: new THREE.Vector3(),
vec7: new THREE.Vector3(),
vec8: new THREE.Vector3(),
quat1: new THREE.Quaternion(),
quat2: new THREE.Quaternion(),
}
const Canvas = {
outlineMaterial: new THREE.LineBasicMaterial({
linewidth: 2,

View File

@ -654,10 +654,7 @@ const UVEditor = {
this.message('uv_editor.turned');
this.loadData();
},
setAutoSize(event) {
var scope = this;
var top2, left2;
setAutoSize(event) {
let vec1 = new THREE.Vector3(),
vec2 = new THREE.Vector3(),
vec3 = new THREE.Vector3(),
@ -666,8 +663,9 @@ const UVEditor = {
plane = new THREE.Plane();
this.getMappableElements().forEach(obj => {
var top2, left2;
if (obj instanceof Cube) {
scope.getFaces(obj, event).forEach(function(side) {
this.getFaces(obj, event).forEach(function(side) {
let face = obj.faces[side];
let mirror_x = face.uv[0] > face.uv[2];
let mirror_y = face.uv[1] > face.uv[3];
@ -686,8 +684,8 @@ const UVEditor = {
if (face.rotation % 180) {
[left2, top2] = [top2, left2];
}
left2 *= scope.getResolution(0, face) / Project.texture_width;
top2 *= scope.getResolution(1, face) / Project.texture_height;
left2 *= this.getResolution(0, face) / Project.texture_width;
top2 *= this.getResolution(1, face) / Project.texture_height;
face.uv_size = [left2, top2];
if (mirror_x) [face.uv[0], face.uv[2]] = [face.uv[2], face.uv[0]];
if (mirror_y) [face.uv[1], face.uv[3]] = [face.uv[3], face.uv[1]];
@ -695,7 +693,7 @@ const UVEditor = {
obj.autouv = 0
} else if (obj instanceof Mesh) {
scope.getFaces(obj, event).forEach(fkey => {
this.getFaces(obj, event).forEach(fkey => {
let face = obj.faces[fkey];
let vertex_uvs = {};
let uv_center = [0, 0];
@ -1475,11 +1473,16 @@ Interface.definePanels(function() {
bottom: Toolbars.UVEditor
},
onResize: function() {
UVEditor.vue.updateSize()
UVEditor.vue.updateSize();
UVEditor.vue.hidden = !this.isVisible();
},
onFold: function() {
UVEditor.vue.hidden = !this.isVisible();
},
component: {
data() {return {
mode: 'uv',
hidden: false,
box_uv: false,
width: 320,
height: 320,
@ -1953,6 +1956,7 @@ Interface.definePanels(function() {
@mouseleave="if (mode == 'paint') mouse_coords.x = -1"
class="checkerboard_target"
ref="viewport"
v-show="!hidden"
:style="{width: (width+8) + 'px', height: (height+8) + 'px', overflowX: (zoom > 1) ? 'scroll' : 'hidden', overflowY: (inner_height > height) ? 'scroll' : 'hidden'}"
>
@ -2030,8 +2034,8 @@ Interface.definePanels(function() {
<div id="uv_brush_outline" v-if="mode == 'paint' && mouse_coords.x >= 0" :style="getBrushOutlineStyle()"></div>
<img style="object-fit: cover; object-position: 0px 0px;" v-if="texture && texture.error != 1" :src="texture.source">
<img style="object-fit: cover; object-position: 0px 0px; opacity: 0.02; mix-blend-mode: screen;" v-if="texture == 0 && !box_uv" src="./assets/missing_blend.png">
<img style="object-fit: cover;" :style="{objectPosition: \`0 -\${texture.currentFrame * inner_height}px\`}" v-if="texture && texture.error != 1" :src="texture.source">
<img style="object-fit: cover; opacity: 0.02; mix-blend-mode: screen;" v-if="texture == 0 && !box_uv" src="./assets/missing_blend.png">
</div>
<div class="uv_transparent_face" v-else-if="selected_faces.length">${tl('uv_editor.transparent_face')}</div>

View File

@ -35,28 +35,16 @@ function loadInfoFromURL() {
//Misc
window.onbeforeunload = function() {
if (Project.saved === false && elements.length > 0) {
let unsaved_projects = ModelProject.all.find(project => {
return !project.saved || project.textures.find(tex => !tex.saved)
})
if (unsaved_projects) {
return 'Unsaved Changes';
} else {
Blockbench.dispatchEvent('before_closing')
if (Project.EditSession) Project.EditSession.quit()
}
}
function showSaveDialog(close) {
var unsaved_textures = 0;
Texture.all.forEach(function(t) {
if (!t.saved) {
unsaved_textures++;
}
})
if ((Project.saved === false && elements.length > 0) || unsaved_textures) {
var answer = confirm(tl('message.close_warning.web'))
return answer;
} else {
return true;
}
}
function setupMobilePanelSelector() {
if (Blockbench.isMobile) {