diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 032a79a1e..ed6910396 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -163,6 +163,8 @@ window.qBittorrent.DynamicTable ??= (() => { }, true); this.dynamicTableDiv.addEventListener("touchstart", (e) => { + if (e.target.classList.contains("selectRowCheckbox")) + return; // ignore touch events on checkboxes, otherwise breaks on mobile const tr = e.target.closest("tr"); if (!tr) return; @@ -588,6 +590,8 @@ window.qBittorrent.DynamicTable ??= (() => { column["onVisibilityChange"] = null; column["staticWidth"] = null; column["calculateBuffer"] = () => 0; + column["jsManaged"] = false; + this.columns.push(column); this.columns[name] = column; @@ -736,10 +740,12 @@ window.qBittorrent.DynamicTable ??= (() => { for (const row of this.getFilteredAndSortedRows()) this.selectedRows.push(row.rowId); this.setRowClass(); + this.onSelectedRowChanged(); } deselectAll() { this.selectedRows.empty(); + this.onSelectedRowChanged(); } selectRow(rowId) { @@ -1000,7 +1006,10 @@ window.qBittorrent.DynamicTable ??= (() => { tds[i].style.width = `${this.columns[i].width}px`; tds[i].style.maxWidth = `${this.columns[i].width}px`; } - if (this.columns[i].dataProperties.some(prop => Object.hasOwn(data, prop))) + if ( + this.columns[i].dataProperties.some(prop => Object.hasOwn(data, prop)) + || (this.columns[i].jsManaged) + ) this.columns[i].updateTd(tds[i], row); } row["data"] = {}; @@ -1096,12 +1105,18 @@ window.qBittorrent.DynamicTable ??= (() => { } class TorrentsTable extends DynamicTable { + setupVirtualList() { super.setupVirtualList(); this.rowHeight = 22; } initColumns() { + if (LocalPreferences.get("use_checkboxes_in_ui", "false") === "true") { + this.newColumn("selected", "width: 20px; text-align: center;", "", 20, true); + this.columns["selected"].jsManaged = true; + } + this.newColumn("priority", "", "#", 30, true); this.newColumn("state_icon", "", "QBT_TR(Status Icon)QBT_TR[CONTEXT=TransferListModel]", 30, false); this.newColumn("name", "", "QBT_TR(Name)QBT_TR[CONTEXT=TransferListModel]", 200, true); @@ -1201,6 +1216,29 @@ window.qBittorrent.DynamicTable ??= (() => { return `stateIcon ${stateClass}`; }; + if (LocalPreferences.get("use_checkboxes_in_ui", "false") === "true") { + const self = this; + this.columns["selected"].updateTd = (td, row) => { + let htmlInput = td.firstElementChild; + if (htmlInput === null) + htmlInput = document.createElement("input"); + + htmlInput.type = "checkbox"; + htmlInput.className = "selectRowCheckbox"; + htmlInput.checked = self.isRowSelected(row.rowId); + + if (td.firstElementChild === null) { + htmlInput.addEventListener("click", (e) => { + e.stopPropagation(); + if (htmlInput.checked) + self.selectRow(row.rowId); + else + self.deselectRow(row.rowId); + }); + td.append(htmlInput); + } + }; + } // state_icon this.columns["state_icon"].updateTd = function(td, row) { @@ -1817,6 +1855,15 @@ window.qBittorrent.DynamicTable ??= (() => { onSelectedRowChanged() { updatePropertiesPanel(); + this.updateSelectedCheckboxes(); + } + + updateSelectedCheckboxes() { + const checkboxes = document.getElementsByClassName("selectRowCheckbox"); + for (const checkbox of checkboxes) { + const rowId = checkbox.closest("tr").rowId; + checkbox.checked = this.isRowSelected(rowId); + } } } diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 26a5389ac..e827c9c39 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -1031,6 +1031,10 @@
+