Tab bar: add tab sorting

This commit is contained in:
JannisX11 2021-05-08 16:52:49 +02:00
parent ca0b1d6377
commit 3ed3511af8
4 changed files with 119 additions and 10 deletions

View File

@ -274,7 +274,6 @@
display: grid; display: grid;
grid-template-rows: auto minmax(200px, 5000px) 26px 36px; grid-template-rows: auto minmax(200px, 5000px) 26px 36px;
grid-template-areas: grid-template-areas:
"tab_bar"
"toolbar" "toolbar"
"center" "center"
"status_bar" "status_bar"
@ -414,6 +413,11 @@
display: inline-flex; display: inline-flex;
margin-left: 2px; margin-left: 2px;
border-top: 2px solid transparent; border-top: 2px solid transparent;
left: 0;
--tabwidth: 202px;
}
#tab_bar.drag_mode .project_tab {
transition: left 100ms ease;
} }
#tab_bar .project_tab.selected { #tab_bar .project_tab.selected {
background-color: var(--color-ui); background-color: var(--color-ui);
@ -424,11 +428,26 @@
background-color: var(--color-button); background-color: var(--color-button);
color: var(--color-light); color: var(--color-light);
} }
#tab_bar .project_tab.dragging {
background-color: var(--color-button);
color: var(--color-light);
position: relative;
z-index: 5;
box-shadow: 0 0 10px #00000080;
transition: none;
}
#tab_bar .project_tab.move_back {
left: calc(var(--tabwidth) * -1);
}
#tab_bar .project_tab.move_forth {
left: var(--tabwidth);
}
#tab_bar .project_tab > label { #tab_bar .project_tab > label {
overflow: hidden; overflow: hidden;
width: calc(100% - 20px); width: calc(100% - 20px);
flex: 1 1 auto; flex: 1 1 auto;
padding: 0 4px; padding: 0 4px;
pointer-events: none;
} }
#tab_bar .project_tab > * { #tab_bar .project_tab > * {
cursor: inherit cursor: inherit

View File

@ -584,8 +584,21 @@
<button id="web_download_button" hidden><a class="tl" href="https://blockbench.net/downloads">web.download_app</a></button> <button id="web_download_button" hidden><a class="tl" href="https://blockbench.net/downloads">web.download_app</a></button>
</header> </header>
<div id="tab_bar"> <div id="tab_bar" :class="{drag_mode: drag_target_index !== null}">
<div class="project_tab" v-for="project in tabs" :key="project.uuid" :class="{selected: project.selected}" @click="project.select()" @dblclick="project.openSettings()"> <div
class="project_tab"
v-for="(project, index) in tabs" :key="project.uuid"
:class="{
selected: project.selected,
dragging: index == drag_target_index,
move_back: (drag_position_index !== null && index > drag_target_index && drag_position_index >= index),
move_forth: (drag_position_index !== null && index < drag_target_index && drag_position_index <= index)
}"
@click="project.select()"
@dblclick="project.openSettings()"
@mousedown="dragTab(project, $event)"
@mouseup="mouseUp(project, $event)"
>
<label :title="project.name || project.geometry_name">{{ project.name || project.geometry_name || project.format.name }}</label> <label :title="project.name || project.geometry_name">{{ project.name || project.geometry_name || project.format.name }}</label>
<div class="project_tab_close_button" :class="{unsaved: !project.saved}" @click="project.close()"> <div class="project_tab_close_button" :class="{unsaved: !project.saved}" @click="project.close()">
<i class="material-icons">{{ project.saved ? 'clear' : 'fiber_manual_record' }}</i> <i class="material-icons">{{ project.saved ? 'clear' : 'fiber_manual_record' }}</i>

View File

@ -100,7 +100,7 @@ const Interface = {
}, },
position: function(line) { position: function(line) {
line.setPosition({ line.setPosition({
top: 26, top: document.getElementById('page_wrapper').offsetTop,
bottom: 0, bottom: 0,
left: Interface.data.left_bar_width+2 left: Interface.data.left_bar_width+2
}) })
@ -123,7 +123,7 @@ const Interface = {
}, },
position: function(line) { position: function(line) {
line.setPosition({ line.setPosition({
top: 56, top: document.getElementById('page_wrapper').offsetTop+30,
bottom: 0, bottom: 0,
right: Interface.data.right_bar_width-2 right: Interface.data.right_bar_width-2
}) })
@ -747,10 +747,6 @@ onVueSetup(function() {
> >
<i class="material-icons">live_tv</i> <i class="material-icons">live_tv</i>
</div> </div>
<div id="status_saved">
<i class="material-icons" v-if="Prop.project_saved" v-bind:title="tl('status_bar.saved')">check</i>
<i class="material-icons" v-else v-bind:title="tl('status_bar.unsaved')">close</i>
</div>
<div v-html="Blockbench.getIconNode(Format.icon).outerHTML" v-bind:title="Format.name"></div> <div v-html="Blockbench.getIconNode(Format.icon).outerHTML" v-bind:title="Format.name"></div>
<div v-if="Prop.recording" v-html="Blockbench.getIconNode('fiber_manual_record').outerHTML" style="color: var(--color-close)" v-bind:title="tl('status_bar.recording')"></div> <div v-if="Prop.recording" v-html="Blockbench.getIconNode('fiber_manual_record').outerHTML" style="color: var(--color-close)" v-bind:title="tl('status_bar.recording')"></div>

View File

@ -277,6 +277,8 @@ onVueSetup(() => {
el: '#tab_bar', el: '#tab_bar',
data: { data: {
projects: ModelProject.all, projects: ModelProject.all,
drag_target_index: null,
drag_position_index: null,
new_tab: { new_tab: {
name: 'New Tab', name: 'New Tab',
saved: true, saved: true,
@ -302,7 +304,6 @@ onVueSetup(() => {
if (this.new_tab.visible) { if (this.new_tab.visible) {
tabs.push(this.new_tab); tabs.push(this.new_tab);
} }
console.log(tabs)
return tabs; return tabs;
} }
}, },
@ -310,6 +311,86 @@ onVueSetup(() => {
openNewTab() { openNewTab() {
this.new_tab.visible = true; this.new_tab.visible = true;
this.new_tab.select(); this.new_tab.select();
},
dragTab(tab, e1) {
convertTouchEvent(e1);
let scope = this;
let active = false;
let timeout;
let last_event = e1;
let tab_node = e1.target;
if (!tab_node.classList.contains('project_tab') || ModelProject.all.indexOf(tab) < 0) return;
let activate = () => {
this.drag_target_index = ModelProject.all.indexOf(tab);
this.drag_position_index = 0;
if (open_menu) open_menu.hide();
active = true;
}
function move(e2) {
convertTouchEvent(e2);
let offset = e2.clientX - e1.clientX;
if (!active) {
let distance = Math.abs(offset);
if (Blockbench.isTouch) {
if (distance > 14 && timeout) {
clearTimeout(timeout);
timeout = null;
} else {
document.getElementById('tab_bar').scrollLeft += last_event.clientX - e2.clientX;
}
} else if (distance > 5) {
activate();
}
} else {
if (e2) e2.preventDefault();
tab_node.style.left = `${offset}px`;
let index_offset = Math.trunc((e2.clientX - e1.clientX) / tab_node.clientWidth);
scope.drag_position_index = scope.drag_target_index + index_offset;
console.log('drag_target_index', scope.drag_target_index, 'drag_position_index', scope.drag_position_index)
}
last_event = e2;
}
function off(e2) {
let {drag_target_index} = scope;
removeEventListeners(document, 'mousemove touchmove', move);
removeEventListeners(document, 'mouseup touchend', off);
tab_node.style.left = null;
scope.drag_target_index = null;
scope.drag_position_index = null;
if (Blockbench.isTouch) clearTimeout(timeout);
if (active && !open_menu) {
convertTouchEvent(e2);
let index_offset = Math.trunc((e2.clientX - e1.clientX) / tab_node.clientWidth);
if (index_offset) {
ModelProject.all.splice(drag_target_index, 1);
ModelProject.all.splice(drag_target_index + index_offset, 0, tab);
}
}
}
if (Blockbench.isTouch) {
timeout = setTimeout(() => {
active = true;
move(e1);
}, 320)
}
addEventListeners(document, 'mousemove touchmove', move, {passive: false});
addEventListeners(document, 'mouseup touchend', off, {passive: false});
},
mouseUp(tab, e1) {
if (e1.button === 1) {
tab.close()
}
} }
} }
}) })