diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 37519058e..742bdaa1e 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -617,7 +617,8 @@ window.qBittorrent.DynamicTable = (function() { onSelectedRowChanged: function() {}, updateRowData: function(data) { - const rowId = data['rowId']; + // ensure rowId is a string + const rowId = `${data['rowId']}`; let row; if (!this.rows.has(rowId)) { @@ -696,8 +697,13 @@ window.qBittorrent.DynamicTable = (function() { this.updateRow(trs[rowPos], fullUpdate); else { // else create a new row in the table const tr = new Element('tr'); + // set tabindex so element receives keydown events + // more info: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event + tr.setProperty("tabindex", "-1"); - tr['rowId'] = rows[rowPos]['rowId']; + const rowId = rows[rowPos]['rowId']; + tr.setProperty("data-row-id", rowId); + tr['rowId'] = rowId; tr._this = this; tr.addEvent('contextmenu', function(e) { @@ -734,6 +740,16 @@ window.qBittorrent.DynamicTable = (function() { } return false; }); + tr.addEvent('keydown', function(event) { + switch (event.key) { + case "up": + this._this.selectPreviousRow(); + return false; + case "down": + this._this.selectNextRow(); + return false; + } + }); this.setupTr(tr); @@ -812,6 +828,50 @@ window.qBittorrent.DynamicTable = (function() { getRowIds: function() { return this.rows.getKeys(); }, + + selectNextRow: function() { + const visibleRows = $(this.dynamicTableDivId).getElements("tbody tr").filter(e => e.getStyle("display") !== "none"); + const selectedRowId = this.getSelectedRowId(); + + let selectedIndex = -1; + for (let i = 0; i < visibleRows.length; ++i) { + const row = visibleRows[i]; + if (row.getProperty("data-row-id") === selectedRowId) { + selectedIndex = i; + break; + } + } + + const isLastRowSelected = (selectedIndex >= (visibleRows.length - 1)); + if (!isLastRowSelected) { + this.deselectAll(); + + const newRow = visibleRows[selectedIndex + 1]; + this.selectRow(newRow.getProperty("data-row-id")); + } + }, + + selectPreviousRow: function() { + const visibleRows = $(this.dynamicTableDivId).getElements("tbody tr").filter(e => e.getStyle("display") !== "none"); + const selectedRowId = this.getSelectedRowId(); + + let selectedIndex = -1; + for (let i = 0; i < visibleRows.length; ++i) { + const row = visibleRows[i]; + if (row.getProperty("data-row-id") === selectedRowId) { + selectedIndex = i; + break; + } + } + + const isFirstRowSelected = selectedIndex <= 0; + if (!isFirstRowSelected) { + this.deselectAll(); + + const newRow = visibleRows[selectedIndex - 1]; + this.selectRow(newRow.getProperty("data-row-id")); + } + }, }); const TorrentsTable = new Class({ @@ -2006,6 +2066,19 @@ window.qBittorrent.DynamicTable = (function() { row.full_data.remaining = 0; else row.full_data.remaining = (row.full_data.size * (1.0 - (row.full_data.progress / 100))); + }, + + setupTr: function(tr) { + tr.addEvent('keydown', function(event) { + switch (event.key) { + case "left": + qBittorrent.PropFiles.collapseFolder(this._this.getSelectedRowId()); + return false; + case "right": + qBittorrent.PropFiles.expandFolder(this._this.getSelectedRowId()); + return false; + } + }); } }); diff --git a/src/webui/www/private/scripts/prop-files.js b/src/webui/www/private/scripts/prop-files.js index 9b8d67ec9..d66092ad2 100644 --- a/src/webui/www/private/scripts/prop-files.js +++ b/src/webui/www/private/scripts/prop-files.js @@ -43,7 +43,9 @@ window.qBittorrent.PropFiles = (function() { createPriorityCombo: createPriorityCombo, updatePriorityCombo: updatePriorityCombo, updateData: updateData, - collapseIconClicked: collapseIconClicked + collapseIconClicked: collapseIconClicked, + expandFolder: expandFolder, + collapseFolder: collapseFolder }; }; @@ -495,6 +497,20 @@ window.qBittorrent.PropFiles = (function() { collapseNode(node); }; + const expandFolder = function(id) { + const node = torrentFilesTable.getNode(id); + if (node.isFolder) { + expandNode(node); + } + }; + + const collapseFolder = function(id) { + const node = torrentFilesTable.getNode(id); + if (node.isFolder) { + collapseNode(node); + } + }; + const filesPriorityMenuClicked = function(priority) { const selectedRows = torrentFilesTable.selectedRowsIds(); if (selectedRows.length === 0) return;