From d3b9e08446708654b3c6fad565b734d93effe683 Mon Sep 17 00:00:00 2001 From: Giulio Ganci Date: Fri, 3 Jun 2022 18:56:19 +0200 Subject: [PATCH 1/2] feat: hotkeys to navigate forward or backward between tabs --- src/renderer/components/Workspace.vue | 24 ++++++++++++++++++++++-- src/renderer/stores/workspaces.js | 20 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/Workspace.vue b/src/renderer/components/Workspace.vue index fec4a3db..fe8ac2f6 100644 --- a/src/renderer/components/Workspace.vue +++ b/src/renderer/components/Workspace.vue @@ -556,7 +556,9 @@ export default { selectTab, newTab, removeTab, - updateTabs + updateTabs, + selectNextTab, + selectPrevTab } = workspacesStore; return { @@ -568,7 +570,9 @@ export default { selectTab, newTab, removeTab, - updateTabs + updateTabs, + selectNextTab, + selectPrevTab }; }, data () { @@ -670,6 +674,22 @@ export default { if (currentTab) this.closeTab(currentTab); } + + // select next tab + if (e.altKey && (e.ctrlKey || e.metaKey) && e.key === 'ArrowRight') + this.selectNextTab({ uid: this.connection.uid }); + + // select prev tab + if (e.altKey && (e.ctrlKey || e.metaKey) && e.key === 'ArrowLeft') + this.selectPrevTab({ uid: this.connection.uid }); + + // select tab by index (range 1-9). CTRL|CMD number + if ((e.ctrlKey || e.metaKey) && !e.altKey && e.keyCode >= 49 && e.keyCode <= 57) { + const newIndex = parseInt(e.key) - 1; + + if (this.workspace.tabs[newIndex]) + this.selectTab({ uid: this.connection.uid, tab: this.workspace.tabs[newIndex].uid }); + } }, openAsPermanentTab (tab) { const permanentTabs = { diff --git a/src/renderer/stores/workspaces.js b/src/renderer/stores/workspaces.js index d3464f98..d1381fae 100644 --- a/src/renderer/stores/workspaces.js +++ b/src/renderer/stores/workspaces.js @@ -609,6 +609,26 @@ export const useWorkspacesStore = defineStore('workspaces', { : workspace ); }, + selectNextTab ({ uid }) { + const workspace = this.workspaces.find(workspace => workspace.uid === uid); + + let newIndex = workspace.tabs.findIndex(tab => tab.selected || tab.uid === workspace.selectedTab) + 1; + + if (newIndex > workspace.tabs.length -1) + newIndex = 0; + + this.selectTab({ uid, tab: workspace.tabs[newIndex].uid }); + }, + selectPrevTab ({ uid }) { + const workspace = this.workspaces.find(workspace => workspace.uid === uid); + + let newIndex = workspace.tabs.findIndex(tab => tab.selected || tab.uid === workspace.selectedTab) - 1; + + if (newIndex < 0) + newIndex = workspace.tabs.length -1; + + this.selectTab({ uid, tab: workspace.tabs[newIndex].uid }); + }, updateTabs ({ uid, tabs }) { this.workspaces = this.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, tabs } From 49abd1ea7f5ec368e9a9201f8fd5b6520c4bd0a8 Mon Sep 17 00:00:00 2001 From: Giulio Ganci Date: Sat, 4 Jun 2022 08:45:48 +0200 Subject: [PATCH 2/2] feat: hotkeys to navigate inside a table resultset --- .../components/WorkspaceTabQueryTable.vue | 143 ++++++++++++++++-- .../components/WorkspaceTabQueryTableRow.vue | 81 +++++++--- src/renderer/scss/themes/dark-theme.scss | 3 +- src/renderer/scss/themes/light-theme.scss | 3 +- 4 files changed, 195 insertions(+), 35 deletions(-) diff --git a/src/renderer/components/WorkspaceTabQueryTable.vue b/src/renderer/components/WorkspaceTabQueryTable.vue index 1f55b7f7..7aa17899 100644 --- a/src/renderer/components/WorkspaceTabQueryTable.vue +++ b/src/renderer/components/WorkspaceTabQueryTable.vue @@ -5,7 +5,6 @@ tabindex="0" :style="{'height': resultsSize+'px'}" @keyup.delete="showDeleteConfirmModal" - @keydown.ctrl.a="selectAllRows($event)" @keydown.esc="deselectRows" > @@ -162,7 +165,9 @@ export default { currentSortDir: 'asc', resultsetIndex: 0, scrollElement: null, - rowHeight: 23 + rowHeight: 23, + selectedField: null, + isEditingRow: false }; }, computed: { @@ -268,9 +273,11 @@ export default { }, mounted () { window.addEventListener('resize', this.resizeResults); + window.addEventListener('keydown', this.onKey); }, unmounted () { window.removeEventListener('resize', this.resizeResults); + window.removeEventListener('keydown', this.onKey); }, methods: { fieldType (cKey) { @@ -447,20 +454,23 @@ export default { return row; }); }, - selectRow (event, row) { - if (event.ctrlKey) { - if (this.selectedRows.includes(row)) - this.selectedRows = this.selectedRows.filter(el => el !== row); + selectRow (event, row, field) { + this.selectedField = field; + const selectedRowId = row._antares_id; + + if (event.ctrlKey || event.metaKey) { + if (this.selectedRows.includes(selectedRowId)) + this.selectedRows = this.selectedRows.filter(el => el !== selectedRowId); else - this.selectedRows.push(row); + this.selectedRows.push(selectedRowId); } else if (event.shiftKey) { if (!this.selectedRows.length) - this.selectedRows.push(row); + this.selectedRows.push(selectedRowId); else { const lastID = this.selectedRows.slice(-1)[0]; const lastIndex = this.sortedResults.findIndex(el => el._antares_id === lastID); - const clickedIndex = this.sortedResults.findIndex(el => el._antares_id === row); + const clickedIndex = this.sortedResults.findIndex(el => el._antares_id === selectedRowId); if (lastIndex > clickedIndex) { for (let i = clickedIndex; i < lastIndex; i++) this.selectedRows.push(this.sortedResults[i]._antares_id); @@ -472,18 +482,20 @@ export default { } } else - this.selectedRows = [row]; + this.selectedRows = [selectedRowId]; }, selectAllRows (e) { if (e.target.classList.contains('editable-field')) return; + this.selectedField = 0; this.selectedRows = this.localResults.reduce((acc, curr) => { acc.push(curr._antares_id); return acc; }, []); }, deselectRows () { - this.selectedRows = []; + if (!this.isEditingRow) + this.selectedRows = []; }, contextMenu (event, cell) { if (event.target.localName === 'input') return; @@ -536,6 +548,113 @@ export default { content: rows, filename }); + }, + onKey (e) { + if (!this.isSelected) + return; + + if (this.isEditingRow) + return; + + if ((e.ctrlKey || e.metaKey) && e.code === 'KeyA' && !e.altKey) + this.selectAllRows(e); + + // row naviation stuff + if ((e.code.includes('Arrow') || e.code === 'Tab') && this.sortedResults.length > 0 && !e.altKey) { + e.preventDefault(); + + const aviableFields= Object.keys(this.sortedResults[0]).slice(0, -1); // removes _antares_id + + if (!this.selectedField) + this.selectedField = aviableFields[0]; + + const selectedId = this.selectedRows[0]; + const selectedIndex = this.sortedResults.findIndex(row => row._antares_id === selectedId); + const selectedFieldIndex = aviableFields.findIndex(field => field === this.selectedField); + let nextIndex = 0; + let nextFieldIndex = 0; + + if (selectedIndex > -1) { + switch (e.code) { + case 'ArrowDown': + nextIndex = selectedIndex + 1; + nextFieldIndex = selectedFieldIndex; + + if (nextIndex > this.sortedResults.length -1) + nextIndex = this.sortedResults.length -1; + + break; + case 'ArrowUp': + nextIndex = selectedIndex - 1; + nextFieldIndex = selectedFieldIndex; + + if (nextIndex < 0) + nextIndex = 0; + + break; + + case 'ArrowRight': + nextIndex = selectedIndex; + nextFieldIndex = selectedFieldIndex + 1; + + if (nextFieldIndex > aviableFields.length -1) + nextFieldIndex = 0; + + break; + + case 'ArrowLeft': + nextIndex = selectedIndex; + nextFieldIndex = selectedFieldIndex - 1; + + if (nextFieldIndex < 0) + nextFieldIndex = aviableFields.length -1; + + break; + + case 'Tab': + nextIndex = selectedIndex; + if (e.shiftKey) { + nextFieldIndex = selectedFieldIndex - 1; + if (nextFieldIndex < 0) + nextFieldIndex = aviableFields.length -1; + } + else { + nextFieldIndex = selectedFieldIndex + 1; + if (nextFieldIndex > aviableFields.length -1) + nextFieldIndex = 0; + } + } + } + + if (this.sortedResults[nextIndex] && nextIndex !== selectedIndex) { + this.selectedRows = [this.sortedResults[nextIndex]._antares_id]; + this.$nextTick(() => this.scrollToCell(this.scrollElement.querySelector('.td.selected'))); + } + + if (aviableFields[nextFieldIndex] && nextFieldIndex !== selectedFieldIndex) { + this.selectedField = aviableFields[nextFieldIndex]; + this.$nextTick(() => this.scrollToCell(this.scrollElement.querySelector('.td.selected'))); + } + } + }, + scrollToCell (el) { + if (!el) return; + const visYMin = this.scrollElement.scrollTop; + const visYMax = this.scrollElement.scrollTop + this.scrollElement.clientHeight - el.clientHeight; + const visXMin = this.scrollElement.scrollLeft; + const visXMax = this.scrollElement.scrollLeft + this.scrollElement.clientWidth - el.clientWidth; + + if (el.offsetTop < visYMin) + this.scrollElement.scrollTop = el.offsetTop; + + else if (el.offsetTop >= visYMax) + this.scrollElement.scrollTop = el.offsetTop - this.scrollElement.clientHeight + el.clientHeight; + + if (el.offsetLeft < visXMin) + this.scrollElement.scrollLeft = el.offsetLeft; + + else if (el.offsetLeft >= visXMax) + this.scrollElement.scrollLeft = el.offsetLeft - this.scrollElement.clientWidth + el.clientWidth; } } }; diff --git a/src/renderer/components/WorkspaceTabQueryTableRow.vue b/src/renderer/components/WorkspaceTabQueryTableRow.vue index daf1d14f..71eecb38 100644 --- a/src/renderer/components/WorkspaceTabQueryTableRow.vue +++ b/src/renderer/components/WorkspaceTabQueryTableRow.vue @@ -2,14 +2,15 @@