This commit is contained in:
Thomas Piccirello 2025-08-17 14:38:52 -07:00 committed by GitHub
commit a511bf56cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 410 additions and 572 deletions

View file

@ -826,10 +826,6 @@ td.statusBarSeparator {
overflow: auto; overflow: auto;
} }
#searchResultsTableDiv {
height: calc(100% - 26px) !important;
}
#searchResults .dynamicTable { #searchResults .dynamicTable {
width: 100%; width: 100%;
} }

View file

@ -310,7 +310,7 @@
const addRowsToTable = (rows, selectedRows) => { const addRowsToTable = (rows, selectedRows) => {
let rowId = 0; let rowId = 0;
const rootNode = new window.qBittorrent.FileTree.FolderNode(); const rootNode = new window.qBittorrent.FileTree.FolderNode();
rootNode.autoCheckFolders = false; rootNode.autoCalculateCheckedState = false;
rows.forEach((row) => { rows.forEach((row) => {
const pathItems = row.path.split(window.qBittorrent.Filesystem.PathSeparator); const pathItems = row.path.split(window.qBittorrent.Filesystem.PathSeparator);
@ -334,7 +334,7 @@
if (folderNode === null) { if (folderNode === null) {
folderNode = new window.qBittorrent.FileTree.FolderNode(); folderNode = new window.qBittorrent.FileTree.FolderNode();
folderNode.autoCheckFolders = false; folderNode.autoCalculateCheckedState = false;
folderNode.rowId = rowId; folderNode.rowId = rowId;
folderNode.path = (parent.path === "") folderNode.path = (parent.path === "")
? folderName ? folderName

File diff suppressed because it is too large Load diff

View file

@ -114,6 +114,7 @@ window.qBittorrent.FileTree ??= (() => {
name = ""; name = "";
path = ""; path = "";
rowId = null; rowId = null;
fileId = null;
size = 0; size = 0;
checked = TriState.Unchecked; checked = TriState.Unchecked;
remaining = 0; remaining = 0;
@ -122,19 +123,42 @@ window.qBittorrent.FileTree ??= (() => {
availability = 0; availability = 0;
depth = 0; depth = 0;
root = null; root = null;
data = null;
isFolder = false; isFolder = false;
children = []; children = [];
isIgnored() {
return this.priority === FilePriority.Ignored;
}
calculateRemaining() {
this.remaining = this.isIgnored() ? 0 : (this.size * (1.0 - (this.progress / 100)));
}
serialize() {
return {
name: this.name,
path: this.path,
fileId: this.fileId,
size: this.size,
checked: this.checked,
remaining: this.remaining,
progress: this.progress,
priority: this.priority,
availability: this.availability
};
}
} }
class FolderNode extends FileNode { class FolderNode extends FileNode {
/** /**
* Will automatically tick the checkbox for a folder if all subfolders and files are also ticked * When true, the folder's `checked` state will be calculately automatically based on its children
*/ */
autoCheckFolders = true; autoCalculateCheckedState = true;
isFolder = true; isFolder = true;
fileId = -1;
addChild(node) { addChild(node) {
node.calculateRemaining();
this.children.push(node); this.children.push(node);
} }
@ -181,22 +205,32 @@ window.qBittorrent.FileTree ??= (() => {
root.checked = TriState.Partial; root.checked = TriState.Partial;
} }
const isIgnored = (child.priority === FilePriority.Ignored); if (!child.isIgnored()) {
if (!isIgnored) {
root.remaining += child.remaining; root.remaining += child.remaining;
root.progress += (child.progress * child.size); root.progress += (child.progress * child.size);
root.availability += (child.availability * child.size); root.availability += (child.availability * child.size);
} }
} }
root.checked = root.autoCheckFolders ? root.checked : TriState.Checked; root.checked = root.autoCalculateCheckedState ? root.checked : TriState.Checked;
root.progress /= root.size; root.progress = (root.size > 0) ? (root.progress / root.size) : 0;
root.availability /= root.size; root.availability = (root.size > 0) ? (root.availability / root.size) : 0;
} }
stack.pop(); stack.pop();
} }
} }
/**
* Recursively recalculate the amount of data remaining to be downloaded.
* This is useful for updating a folder's "remaining" size as files are unchecked/ignored.
*/
calculateRemaining() {
this.remaining = this.children.reduce((sum, node) => {
node.calculateRemaining();
return sum + node.remaining;
}, 0);
}
} }
return exports(); return exports();

View file

@ -84,12 +84,12 @@ window.qBittorrent.TorrentContent ??= (() => {
const getAllChildren = (id, fileId) => { const getAllChildren = (id, fileId) => {
const node = torrentFilesTable.getNode(id); const node = torrentFilesTable.getNode(id);
const rowIds = [node.data.rowId]; const rowIds = [node.rowId];
const fileIds = [node.data.fileId]; const fileIds = [node.fileId];
const getChildFiles = (node) => { const getChildFiles = (node) => {
rowIds.push(node.data.rowId); rowIds.push(node.rowId);
fileIds.push(node.data.fileId); fileIds.push(node.fileId);
if (node.isFolder) { if (node.isFolder) {
node.children.forEach((child) => { node.children.forEach((child) => {
@ -214,8 +214,8 @@ window.qBittorrent.TorrentContent ??= (() => {
}; };
const getComboboxPriority = (id) => { const getComboboxPriority = (id) => {
const row = torrentFilesTable.rows.get(id.toString()); const node = torrentFilesTable.getNode(id.toString());
return normalizePriority(row.full_data.priority, 10); return normalizePriority(node.priority, 10);
}; };
const switchGlobalCheckboxState = (e) => { const switchGlobalCheckboxState = (e) => {
@ -230,8 +230,9 @@ window.qBittorrent.TorrentContent ??= (() => {
setCheckboxUnchecked(checkbox); setCheckboxUnchecked(checkbox);
torrentFilesTable.rows.forEach((row) => { torrentFilesTable.rows.forEach((row) => {
const rowId = row.rowId; const rowId = row.rowId;
const fileId = row.full_data.fileId; const node = torrentFilesTable.getNode(rowId);
const isChecked = (getCheckboxState(rowId) === TriState.Checked); const fileId = node.fileId;
const isChecked = (node.checked === TriState.Checked);
if (isChecked) { if (isChecked) {
rowIds.push(rowId); rowIds.push(rowId);
fileIds.push(fileId); fileIds.push(fileId);
@ -242,8 +243,9 @@ window.qBittorrent.TorrentContent ??= (() => {
setCheckboxChecked(checkbox); setCheckboxChecked(checkbox);
torrentFilesTable.rows.forEach((row) => { torrentFilesTable.rows.forEach((row) => {
const rowId = row.rowId; const rowId = row.rowId;
const fileId = row.full_data.fileId; const node = torrentFilesTable.getNode(rowId);
const isUnchecked = (getCheckboxState(rowId) === TriState.Unchecked); const fileId = node.fileId;
const isUnchecked = (node.checked === TriState.Unchecked);
if (isUnchecked) { if (isUnchecked) {
rowIds.push(rowId); rowIds.push(rowId);
fileIds.push(fileId); fileIds.push(fileId);
@ -285,26 +287,22 @@ window.qBittorrent.TorrentContent ??= (() => {
checkbox.indeterminate = true; checkbox.indeterminate = true;
}; };
const getCheckboxState = (id) => {
const row = torrentFilesTable.rows.get(id.toString());
return Number(row.full_data.checked);
};
const setFilePriority = (ids, fileIds, priority) => { const setFilePriority = (ids, fileIds, priority) => {
priority = normalizePriority(priority); priority = normalizePriority(priority);
if (onFilePriorityChanged) if (onFilePriorityChanged)
onFilePriorityChanged(fileIds, priority); onFilePriorityChanged(fileIds, priority);
const ignore = (priority === FilePriority.Ignored); const nodes = ids.map((id) => {
ids.forEach((id) => { const node = torrentFilesTable.getNode(id.toString());
id = id.toString(); node.priority = priority;
torrentFilesTable.setIgnored(id, ignore); node.checked = triStateFromPriority(priority);
return node;
const row = torrentFilesTable.rows.get(id);
row.full_data.priority = priority;
row.full_data.checked = triStateFromPriority(priority);
}); });
// must update all nodes above before recalculating
for (const node of nodes)
node.calculateRemaining();
}; };
const updateData = (files) => { const updateData = (files) => {
@ -318,7 +316,6 @@ window.qBittorrent.TorrentContent ??= (() => {
size: file.size, size: file.size,
progress: window.qBittorrent.Misc.toFixedPointString((file.progress * 100), 1), progress: window.qBittorrent.Misc.toFixedPointString((file.progress * 100), 1),
priority: normalizePriority(file.priority), priority: normalizePriority(file.priority),
remaining: (ignore ? 0 : (file.size * (1 - file.progress))),
availability: file.availability availability: file.availability
}; };
@ -372,19 +369,17 @@ window.qBittorrent.TorrentContent ??= (() => {
}); });
const isChecked = row.checked ? TriState.Checked : TriState.Unchecked; const isChecked = row.checked ? TriState.Checked : TriState.Unchecked;
const remaining = (row.priority === FilePriority.Ignored) ? 0 : row.remaining;
const childNode = new window.qBittorrent.FileTree.FileNode(); const childNode = new window.qBittorrent.FileTree.FileNode();
childNode.name = row.name; childNode.name = row.name;
childNode.path = row.fileName; childNode.path = row.fileName;
childNode.rowId = rowId; childNode.rowId = rowId;
childNode.fileId = row.fileId;
childNode.size = row.size; childNode.size = row.size;
childNode.checked = isChecked; childNode.checked = isChecked;
childNode.remaining = remaining;
childNode.progress = row.progress; childNode.progress = row.progress;
childNode.priority = row.priority; childNode.priority = row.priority;
childNode.availability = row.availability; childNode.availability = row.availability;
childNode.root = parent; childNode.root = parent;
childNode.data = row;
parent.addChild(childNode); parent.addChild(childNode);
++rowId; ++rowId;
@ -430,6 +425,7 @@ window.qBittorrent.TorrentContent ??= (() => {
const updateComplete = () => { const updateComplete = () => {
// we've finished recursing // we've finished recursing
updateGlobalCheckbox(); updateGlobalCheckbox();
torrentFilesTable.calculateRemaining();
torrentFilesTable.updateTable(true); torrentFilesTable.updateTable(true);
}; };
@ -447,7 +443,7 @@ window.qBittorrent.TorrentContent ??= (() => {
let indeterminateCount = 0; let indeterminateCount = 0;
let desiredComboboxPriority = null; let desiredComboboxPriority = null;
for (const sibling of siblings) { for (const sibling of siblings) {
switch (getCheckboxState(sibling.rowId)) { switch (sibling.checked) {
case TriState.Checked: case TriState.Checked:
++checkedCount; ++checkedCount;
break; break;
@ -465,7 +461,7 @@ window.qBittorrent.TorrentContent ??= (() => {
desiredComboboxPriority = FilePriority.Mixed; desiredComboboxPriority = FilePriority.Mixed;
} }
const currentCheckboxState = getCheckboxState(parent.rowId); const currentCheckboxState = parent.checked;
let desiredCheckboxState = TriState.Unchecked; let desiredCheckboxState = TriState.Unchecked;
if ((indeterminateCount > 0) || ((checkedCount > 0) && (uncheckedCount > 0))) if ((indeterminateCount > 0) || ((checkedCount > 0) && (uncheckedCount > 0)))
desiredCheckboxState = TriState.Partial; desiredCheckboxState = TriState.Partial;
@ -474,9 +470,9 @@ window.qBittorrent.TorrentContent ??= (() => {
const currentComboboxPriority = getComboboxPriority(parent.rowId); const currentComboboxPriority = getComboboxPriority(parent.rowId);
if ((currentCheckboxState !== desiredCheckboxState) || (currentComboboxPriority !== desiredComboboxPriority)) { if ((currentCheckboxState !== desiredCheckboxState) || (currentComboboxPriority !== desiredComboboxPriority)) {
const row = torrentFilesTable.rows.get(parent.rowId.toString()); const node = torrentFilesTable.getNode(parent.rowId.toString());
row.full_data.priority = desiredComboboxPriority; node.priority = desiredComboboxPriority;
row.full_data.checked = desiredCheckboxState; node.checked = desiredCheckboxState;
updateParentFolder(parent.rowId); updateParentFolder(parent.rowId);
} }