diff --git a/css/panels.css b/css/panels.css index 2313df87..c62b1dba 100644 --- a/css/panels.css +++ b/css/panels.css @@ -279,6 +279,7 @@ /*Outliner*/ #cubes_list { padding-top: 1px; + overflow-y: scroll; } #cubes_list > li:last-child { margin-bottom: 180px; @@ -1241,7 +1242,7 @@ margin-bottom: 0; position: relative; border: 4px solid var(--color-ui); - box-shadow: 0 0 0 800px var(--color-back); + box-shadow: 0 0 0 1800px var(--color-back); box-sizing: content-box; --color-uv-unselected: var(--color-grid); --color-uv-selected: white; @@ -1476,9 +1477,16 @@ cursor: url('../assets/rotate_cursor.png') 9 9, auto; } + .joined_uv_bar { + display: flex; + } + .joined_uv_bar > * { + flex: 1 0 0; + } + .panel .bar.next_to_title { margin-top: -34px; - margin-right: 54px; + margin-right: 78px; position: relative; float: right; pointer-events: none; diff --git a/js/interface/actions.js b/js/interface/actions.js index 497d5918..55e3db46 100644 --- a/js/interface/actions.js +++ b/js/interface/actions.js @@ -2003,7 +2003,6 @@ const BARS = { children: [ 'move_texture_with_uv', 'uv_apply_all', - 'uv_apply_all', 'uv_maximize', 'uv_auto', 'uv_transparent', diff --git a/js/interface/interface.js b/js/interface/interface.js index 45a59997..6ab59619 100644 --- a/js/interface/interface.js +++ b/js/interface/interface.js @@ -203,7 +203,7 @@ const Interface = { }, set(o, diff) { let panel = Interface.getTopPanel(); - panel.position_data.height = limitNumber(o + diff, 150, document.body.clientHeight-120); + panel.position_data.height = limitNumber(o + diff, 150, document.body.clientHeight-200); panel.update(); }, position() {this.setPosition({ @@ -220,7 +220,7 @@ const Interface = { 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().position_data.height = limitNumber(o - diff, 150, document.body.clientHeight-200); Interface.getBottomPanel().update(); }, position() {this.setPosition({ diff --git a/js/interface/panels.js b/js/interface/panels.js index 3094b8bc..e81e3100 100644 --- a/js/interface/panels.js +++ b/js/interface/panels.js @@ -22,6 +22,7 @@ class Panel { if (!Interface.data.panels[this.id]) Interface.data.panels[this.id] = {}; this.position_data = Interface.data.panels[this.id]; let defaultp = data.default_position || 0; + if (defaultp && defaultp.slot) this.previous_slot = defaultp.slot; 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]; @@ -73,6 +74,18 @@ class Panel { } if (!Blockbench.isMobile) { + if (data.expand_button) { + let expand_button = Interface.createElement('div', {class: 'tool panel_control panel_expanding_button'}, Blockbench.getIconNode('fullscreen')) + this.handle.append(expand_button); + expand_button.addEventListener('click', (e) => { + if (this.slot == 'float') { + this.moveTo(this.previous_slot); + } else { + this.moveTo('float'); + this.moveToFront(); + } + }) + } let snap_button = Interface.createElement('div', {class: 'tool panel_control'}, Blockbench.getIconNode('drag_handle')) this.handle.append(snap_button); @@ -355,6 +368,9 @@ class Panel { slot = ref_panel.position_data.slot; } this.node.classList.remove('floating'); + if (slot !== this.slot) { + this.previous_slot = this.slot; + } if (slot == 'left_bar' || slot == 'right_bar') { if (!ref_panel && Interface.data[slot].includes(this.id)) { @@ -378,7 +394,7 @@ class Panel { Interface.data[slot].splice(Interface.data[slot].indexOf(ref_panel.id) + (before ? 0 : 1), 0, this.id); } else { document.getElementById(slot).append(this.node); - Interface.data[slot].push(this.id); + Interface.data[slot].safePush(this.id); } } else if (slot == 'top') { diff --git a/js/texturing/uv.js b/js/texturing/uv.js index 9d248d8b..417d5e1e 100644 --- a/js/texturing/uv.js +++ b/js/texturing/uv.js @@ -314,9 +314,10 @@ const UVEditor = { let pixel_size = UVEditor.getPixelSize(); let focus = [min_x+max_x, min_y+max_y].map(v => v * 0.5 * pixel_size); let {viewport} = UVEditor.vue.$refs; + let margin = UVEditor.vue.getFrameMargin(); $(viewport).animate({ - scrollLeft: focus[0] - UVEditor.width / 2, - scrollTop: focus[1] - UVEditor.height / 2, + scrollLeft: focus[0] + margin[0] - UVEditor.width / 2, + scrollTop: focus[1] + margin[1] - UVEditor.height / 2, }, 100) }, //Get @@ -426,11 +427,12 @@ const UVEditor = { }) Mesh.all.forEach(mesh => { if (mesh.locked) return; - for (var face in mesh.faces) { - let rect = mesh.faces[face].getBoundingRect(); - if (uv && Math.isBetween(u, rect.ax, rect.bx) && Math.isBetween(v, rect.ay, rect.by) && (mesh.faces[face].getTexture() === scope.vue.texture || Format.single_texture)) { + for (var fkey in mesh.faces) { + let face = mesh.faces[fkey]; + let rect = face.getBoundingRect(); + if (face.uv && Math.isBetween(u, rect.ax, rect.bx) && Math.isBetween(v, rect.ay, rect.by) && (face.getTexture() === scope.vue.texture || Format.single_texture)) { matches.safePush(mesh); - face_matches.safePush(face); + face_matches.safePush(fkey); break; } } @@ -1670,6 +1672,7 @@ Interface.definePanels(function() { UVEditor.panel = new Panel('uv', { icon: 'photo_size_select_large', selection_only: true, + expand_button: true, condition: {modes: ['edit', 'paint']}, display_condition: () => UVEditor.getMappableElements().length, default_position: { @@ -1696,6 +1699,7 @@ Interface.definePanels(function() { width: 320, height: 320, zoom: 1, + centered_view: true, checkerboard: settings.uv_checkerboard.value, uv_overlay: false, texture: 0, @@ -1728,10 +1732,20 @@ Interface.definePanels(function() { }}, computed: { inner_width() { - return this.width * this.zoom; + let axis = this.project_resolution[0] / this.project_resolution[1] < this.width / this.height; + if (axis) { + return this.height * this.zoom * (this.project_resolution[1] / this.project_resolution[0]); + } else { + return this.width * this.zoom; + } }, inner_height() { - return this.width * (this.project_resolution[1] / this.project_resolution[0]) * this.zoom; + let axis = this.project_resolution[0] / this.project_resolution[1] < this.width / this.height; + if (axis) { + return this.height * this.zoom; + } else { + return this.width * this.zoom * (this.project_resolution[1] / this.project_resolution[0]); + } }, mappable_elements() { return this.elements.filter(element => element.faces && !element.locked); @@ -1757,17 +1771,25 @@ Interface.definePanels(function() { if (!this.$refs.viewport) return; let old_size = this.width; let size = Math.floor(Math.clamp(UVEditor.panel.width - 10, 64, 1e5)); - let sidebar = Panels.uv.slot.includes('_bar'); this.width = size; - this.height = sidebar - ? size * Math.clamp(this.project_resolution[1] / this.project_resolution[0], 0.5, 1) - : Math.clamp(UVEditor.panel.height - UVEditor.panel.handle.clientHeight - 30 - (this.mode == 'uv' ? 30 : 0) - 8, 64, 1e5); + if (Panels.uv.slot.includes('_bar')) { + this.height = size * Math.clamp(this.project_resolution[1] / this.project_resolution[0], 0.5, 1); + } else { + this.height = Math.clamp( + UVEditor.panel.height + -UVEditor.panel.handle.clientHeight - 8 + -(this.$refs.uv_cube_face_bar ? this.$refs.uv_cube_face_bar.clientHeight : 0) + -(this.$refs.uv_toolbars ? this.$refs.uv_toolbars.clientHeight : 0), + 64, 1e5); + } this.$refs.viewport.scrollLeft = Math.round(this.$refs.viewport.scrollLeft * (size / old_size)); this.$refs.viewport.scrollTop = Math.round(this.$refs.viewport.scrollTop * (size / old_size)); + let slider_bar_width = UVEditor.vue.$refs.slider_bar.clientWidth - 6; + for (var id in UVEditor.sliders) { var slider = UVEditor.sliders[id]; - slider.setWidth(size/(Project.box_uv?2:4)-1) + slider.setWidth(slider_bar_width / (Project.box_uv?2:4)-1); } }, setMode(mode) { @@ -1820,9 +1842,11 @@ Interface.definePanels(function() { event.stopPropagation() event.preventDefault() + let original_margin = this.getFrameMargin(); var n = (event.deltaY < 0) ? 0.1 : -0.1; n *= this.zoom - var number = Math.clamp(this.zoom + n, Math.min(1, this.inner_width/this.inner_height), this.max_zoom) + let min = Math.min(1, this.inner_width/this.inner_height) + var number = Math.clamp(this.zoom + n, min, this.max_zoom) let old_zoom = this.zoom; this.zoom = number; @@ -1830,15 +1854,16 @@ Interface.definePanels(function() { let updateScroll = () => { let {viewport} = this.$refs; let offset = $(this.$refs.viewport).offset() - let offsetX = event.clientX - offset.left; - let offsetY = event.clientY - offset.top; + let margin = this.getFrameMargin(); + let offsetX = event.clientX - offset.left - margin[0]; + let offsetY = event.clientY - offset.top - margin[1]; // Make it a bit easier to scroll into corners - offsetX = (offsetX - this.width/2) * 1.1 + this.width/2; - offsetY = (offsetY - this.height/2) * 1.1 + this.height/2; + offsetX = (offsetX - this.width/2 + margin[0]) * 1.1 + this.width/2 - margin[0]; + offsetY = (offsetY - this.height/2 + margin[1]) * 1.1 + this.height/2 - margin[1]; let zoom_diff = this.zoom - old_zoom; - viewport.scrollLeft += ((viewport.scrollLeft + offsetX) * zoom_diff) / old_zoom - viewport.scrollTop += ((viewport.scrollTop + offsetY) * zoom_diff) / old_zoom + viewport.scrollLeft += ((viewport.scrollLeft + offsetX) * zoom_diff) / old_zoom + margin[0] - original_margin[0]; + viewport.scrollTop += ((viewport.scrollTop + offsetY) * zoom_diff) / old_zoom + margin[1] - original_margin[1]; this.updateMouseCoords(event) if (Painter.selection.overlay) UVEditor.updatePastingOverlay() @@ -1856,11 +1881,17 @@ Interface.definePanels(function() { setActivePanel('uv'); if (event.which === 2) { let {viewport} = this.$refs; - let coords = {x: 0, y: 0} + let margin = this.getFrameMargin(); + let margin_center = [this.width/2, this.height/2]; + let original = [ + viewport.scrollLeft, + viewport.scrollTop + ]; function dragMouseWheel(e2) { - viewport.scrollLeft -= (e2.pageX - coords.x) - viewport.scrollTop -= (e2.pageY - coords.y) - coords = {x: e2.pageX, y: e2.pageY} + viewport.scrollLeft = Math.snapToValues(original[0] + event.clientX - e2.clientX, [margin[0], margin_center[0]], 10); + viewport.scrollTop = Math.snapToValues(original[1] + event.clientY - e2.clientY, [margin[1], margin_center[1]], 10); + UVEditor.vue.centered_view = (viewport.scrollLeft == margin[0] || viewport.scrollLeft == margin_center[0]) + && (viewport.scrollTop == margin[1] || viewport.scrollTop == margin_center[1]); } function dragMouseWheelStop(e) { removeEventListeners(document, 'mousemove touchmove', dragMouseWheel); @@ -1868,7 +1899,6 @@ Interface.definePanels(function() { } addEventListeners(document, 'mousemove touchmove', dragMouseWheel); addEventListeners(document, 'mouseup touchend', dragMouseWheelStop); - coords = {x: event.pageX, y: event.pageY} event.preventDefault(); return false; } else if (this.mode == 'paint' && Toolbox.selected.paintTool && (event.which === 1 || (event.touches && event.touches.length == 1))) { @@ -2579,6 +2609,21 @@ Interface.definePanels(function() { } else { return {display: 'none'}; } + }, + getFrameMargin(style) { + let gap_x = Math.max((this.width - this.inner_width) / 2, 0); + let gap_y = Math.max((this.height - this.inner_height) / 2, 0); + let margin = [ + Math.floor(gap_x + this.width/2), + Math.floor(gap_y + this.height/2), + ]; + if (this.$refs.viewport && this.zoom == 1 && ((!this.$refs.viewport.scrollLeft && !this.$refs.viewport.scrollTop) || this.centered_view)) { + this.$refs.viewport.scrollLeft = margin[0] - gap_x; + this.$refs.viewport.scrollTop = margin[1] - gap_y; + this.centered_view = true; + } + + return style ? `${margin[1]}px ${margin[0]}px` : margin; } }, template: ` @@ -2590,7 +2635,7 @@ Interface.definePanels(function() { -