Fully functioning File Browser

This commit is contained in:
Fillip
2024-08-23 00:27:53 -07:00
parent 3bb746730d
commit fc5aa3866e
2 changed files with 132 additions and 48 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ class FL_FileBrowser:
def get_directory_structure(path):
structure = {"name": os.path.basename(path), "children": [], "path": path}
structure = {"name": os.path.basename(path), "children": [], "path": path, "expanded": False}
try:
with os.scandir(path) as entries:
for entry in entries:
+131 -47
View File
@@ -10,29 +10,36 @@ app.registerExtension({
});
function addFileBrowserUI(node) {
// Tweakable variables
const DIRECTORY_Y_OFFSET = 20; // Adjust this to move directory items up or down
const CLICK_Y_OFFSET = 8; // Adjust this to fine-tune click detection
const rootDirectoryWidget = node.widgets.find(w => w.name === "root_directory");
const selectedFileWidget = node.widgets.find(w => w.name === "selected_file");
rootDirectoryWidget.hidden = false;
selectedFileWidget.hidden = true;
const MIN_WIDTH = 400;
const MIN_HEIGHT = 450;
const TOP_PADDING = 140;
const MIN_WIDTH = 730;
const MIN_HEIGHT = 850;
const TOP_PADDING = 150;
const BOTTOM_PADDING = 20;
const FOLDER_HEIGHT = 20;
const FOLDER_HEIGHT = 25;
const INDENT_WIDTH = 15;
const TOP_BAR_HEIGHT = 30;
const TOP_BAR_HEIGHT = 40;
const THUMBNAIL_SIZE = 80;
const THUMBNAIL_PADDING = 5;
const SCROLLBAR_WIDTH = 13;
let currentDirectory = rootDirectoryWidget.value;
let selectedFile = selectedFileWidget.value;
let directoryStructure = { name: "root", children: [] };
let directoryStructure = { name: "root", children: [], expanded: true, path: currentDirectory };
let fileList = [];
let thumbnails = {};
let scrollOffsetLeft = 0;
let scrollOffsetRight = 0;
let isDraggingLeft = false;
let isDraggingRight = false;
async function updateDirectoryStructure() {
try {
@@ -42,7 +49,7 @@ function addFileBrowserUI(node) {
body: JSON.stringify({ path: currentDirectory })
});
const data = await response.json();
directoryStructure = data.structure;
mergeDirectoryStructure(directoryStructure, data.structure);
fileList = data.files;
loadThumbnails();
node.setDirtyCanvas(true);
@@ -51,6 +58,22 @@ function addFileBrowserUI(node) {
}
}
function mergeDirectoryStructure(existing, updated) {
existing.name = updated.name;
existing.path = updated.path;
const existingChildren = new Map(existing.children.map(child => [child.name, child]));
existing.children = updated.children.map(updatedChild => {
const existingChild = existingChildren.get(updatedChild.name);
if (existingChild) {
mergeDirectoryStructure(existingChild, updatedChild);
return existingChild;
} else {
return { ...updatedChild, expanded: false };
}
});
}
async function loadThumbnails() {
thumbnails = {};
for (const file of fileList) {
@@ -105,44 +128,70 @@ function addFileBrowserUI(node) {
ctx.fillRect(5, pos + 5, 60, TOP_BAR_HEIGHT - 10);
ctx.fillStyle = "#FFFFFF";
ctx.font = "12px Arial";
ctx.fillText("← Back", 15, pos + 20);
ctx.fillText("← Back", 15, pos + 25);
// Draw current directory
ctx.fillStyle = "#FFFFFF";
ctx.font = "12px Arial";
ctx.fillText(currentDirectory, 75, pos + 20);
ctx.font = "14px Arial";
ctx.fillText(currentDirectory, 75, pos + 27);
const midX = this.size[0] / 2;
// Set up clipping regions for scrolling
ctx.save();
ctx.beginPath();
ctx.rect(0, TOP_PADDING, midX, this.size[1] - TOP_PADDING - BOTTOM_PADDING);
ctx.rect(0, TOP_PADDING, midX - SCROLLBAR_WIDTH, this.size[1] - TOP_PADDING - BOTTOM_PADDING);
ctx.clip();
drawDirectoryStructure(ctx, 10, TOP_PADDING - scrollOffsetLeft, directoryStructure);
drawDirectoryStructure(ctx, 10, TOP_PADDING - scrollOffsetLeft + DIRECTORY_Y_OFFSET, directoryStructure);
ctx.restore();
ctx.save();
ctx.beginPath();
ctx.rect(midX, TOP_PADDING, this.size[0] - midX, this.size[1] - TOP_PADDING - BOTTOM_PADDING);
ctx.rect(midX, TOP_PADDING, this.size[0] - midX - SCROLLBAR_WIDTH, this.size[1] - TOP_PADDING - BOTTOM_PADDING);
ctx.clip();
drawThumbnails(ctx, midX, TOP_PADDING - scrollOffsetRight, this.size[0] / 2 - 10, this.size[1] - TOP_PADDING - BOTTOM_PADDING);
drawThumbnails(ctx, midX, TOP_PADDING - scrollOffsetRight, this.size[0] / 2 - SCROLLBAR_WIDTH - 10, this.size[1] - TOP_PADDING - BOTTOM_PADDING);
ctx.restore();
// Draw scrollbars
drawScrollbar(ctx, midX - SCROLLBAR_WIDTH, TOP_PADDING, SCROLLBAR_WIDTH, this.size[1] - TOP_PADDING - BOTTOM_PADDING, scrollOffsetLeft, getTotalDirectoryHeight());
drawScrollbar(ctx, this.size[0] - SCROLLBAR_WIDTH, TOP_PADDING, SCROLLBAR_WIDTH, this.size[1] - TOP_PADDING - BOTTOM_PADDING, scrollOffsetRight, getTotalThumbnailHeight());
}
};
function drawScrollbar(ctx, x, y, width, height, offset, totalHeight) {
ctx.fillStyle = "#555555";
ctx.fillRect(x, y, width, height);
const visibleHeight = height;
const scrollHeight = Math.max(height * (visibleHeight / totalHeight), 20);
const maxOffset = Math.max(0, totalHeight - visibleHeight);
const scrollY = y + (offset / maxOffset) * (height - scrollHeight);
ctx.fillStyle = "#888888";
ctx.fillRect(x, scrollY, width, scrollHeight);
}
function getTotalDirectoryHeight() {
return countItems(directoryStructure) * FOLDER_HEIGHT;
}
function getTotalThumbnailHeight() {
const thumbnailsPerRow = Math.floor((node.size[0] / 2 - SCROLLBAR_WIDTH - 10) / (THUMBNAIL_SIZE + THUMBNAIL_PADDING));
return Math.ceil(fileList.length / thumbnailsPerRow) * (THUMBNAIL_SIZE + THUMBNAIL_PADDING);
}
function drawDirectoryStructure(ctx, x, y, structure, level = 0) {
const folderIcon = "📁";
const folderIcon = structure.expanded ? "📂" : "📁";
ctx.font = "14px Arial";
ctx.fillStyle = "#ffffff";
ctx.fillStyle = structure.path === currentDirectory ? "#4a90e2" : "#ffffff";
for (const item of structure.children) {
const xPos = x + INDENT_WIDTH * level;
ctx.fillText(`${folderIcon} ${item.name}`, xPos, y);
y += FOLDER_HEIGHT;
const xPos = x + INDENT_WIDTH * level;
ctx.fillText(`${folderIcon} ${structure.name}`, xPos, y);
y += FOLDER_HEIGHT;
if (item.children) {
y = drawDirectoryStructure(ctx, x, y, item, level + 1);
if (structure.expanded && structure.children) {
for (const child of structure.children) {
y = drawDirectoryStructure(ctx, x, y, child, level + 1);
}
}
@@ -181,9 +230,13 @@ function addFileBrowserUI(node) {
node.onMouseDown = function(event) {
const pos = TOP_PADDING - TOP_BAR_HEIGHT;
const localY = event.canvasY - this.pos[1] - pos;
const localY = event.canvasY - this.pos[1] - pos + CLICK_Y_OFFSET;
const localX = event.canvasX - this.pos[0];
if (localY < 0 || localY > this.size[1] || localX < 0 || localX > this.size[0]) {
return false; // Allow default behavior for dragging the node
}
if (localY >= 0 && localY <= TOP_BAR_HEIGHT) {
// Click on top bar
if (localX >= 5 && localX <= 65 && localY >= 5 && localY <= TOP_BAR_HEIGHT - 5) {
@@ -193,57 +246,88 @@ function addFileBrowserUI(node) {
}
} else if (localY > TOP_BAR_HEIGHT) {
const midX = this.size[0] / 2;
if (localX < midX) {
if (localX < midX - SCROLLBAR_WIDTH) {
// Click on directory structure
const clickedItem = findClickedItem(directoryStructure, localY - TOP_BAR_HEIGHT + scrollOffsetLeft);
const clickedItem = findClickedItem(directoryStructure, localY - TOP_BAR_HEIGHT + scrollOffsetLeft - DIRECTORY_Y_OFFSET);
if (clickedItem) {
currentDirectory = clickedItem.path;
rootDirectoryWidget.value = currentDirectory;
updateDirectoryStructure();
if (clickedItem.children) {
clickedItem.expanded = !clickedItem.expanded;
this.setDirtyCanvas(true);
}
if (clickedItem.path !== currentDirectory) {
currentDirectory = clickedItem.path;
rootDirectoryWidget.value = currentDirectory;
updateDirectoryStructure();
}
}
} else {
return true;
} else if (localX >= midX && localX < this.size[0] - SCROLLBAR_WIDTH) {
// Click on thumbnails
const thumbnailsPerRow = Math.floor((this.size[0] / 2 - 10) / (THUMBNAIL_SIZE + THUMBNAIL_PADDING));
const thumbnailsPerRow = Math.floor((this.size[0] / 2 - SCROLLBAR_WIDTH - 10) / (THUMBNAIL_SIZE + THUMBNAIL_PADDING));
const clickedRow = Math.floor((localY - TOP_BAR_HEIGHT + scrollOffsetRight) / (THUMBNAIL_SIZE + THUMBNAIL_PADDING));
const clickedCol = Math.floor((localX - midX) / (THUMBNAIL_SIZE + THUMBNAIL_PADDING));
const clickedIndex = clickedRow * thumbnailsPerRow + clickedCol;
if (clickedIndex >= 0 && clickedIndex < fileList.length) {
updateSelectedFile(fileList[clickedIndex]);
}
return true;
} else if (localX >= midX - SCROLLBAR_WIDTH && localX < midX) {
// Click on left scrollbar
isDraggingLeft = true;
return true;
} else if (localX >= this.size[0] - SCROLLBAR_WIDTH) {
// Click on right scrollbar
isDraggingRight = true;
return true;
}
}
return true;
return false; // Allow default behavior for dragging the node
};
node.onMouseMove = function(event) {
if (event.dragging && event.button === 2) { // Right mouse button
const midX = this.size[0] / 2;
if (event.canvasX - this.pos[0] < midX) {
scrollOffsetLeft = Math.max(0, scrollOffsetLeft - event.deltay);
} else {
scrollOffsetRight = Math.max(0, scrollOffsetRight - event.deltay);
}
if (isDraggingLeft) {
const totalHeight = getTotalDirectoryHeight();
const visibleHeight = this.size[1] - TOP_PADDING - BOTTOM_PADDING;
const maxOffset = Math.max(0, totalHeight - visibleHeight);
scrollOffsetLeft = Math.max(0, Math.min(maxOffset, (event.canvasY - this.pos[1] - TOP_PADDING) / visibleHeight * totalHeight));
this.setDirtyCanvas(true);
return true;
} else if (isDraggingRight) {
const totalHeight = getTotalThumbnailHeight();
const visibleHeight = this.size[1] - TOP_PADDING - BOTTOM_PADDING;
const maxOffset = Math.max(0, totalHeight - visibleHeight);
scrollOffsetRight = Math.max(0, Math.min(maxOffset, (event.canvasY - this.pos[1] - TOP_PADDING) / visibleHeight * totalHeight));
this.setDirtyCanvas(true);
return true;
}
return false;
};
node.onMouseUp = function(event) {
isDraggingLeft = false;
isDraggingRight = false;
return false;
};
function findClickedItem(structure, y, currentY = 0) {
for (const item of structure.children) {
if (y >= currentY && y < currentY + FOLDER_HEIGHT) {
return item;
}
currentY += FOLDER_HEIGHT;
if (item.children) {
const found = findClickedItem(item, y, currentY);
if (y >= currentY && y < currentY + FOLDER_HEIGHT) {
return structure;
}
currentY += FOLDER_HEIGHT;
if (structure.expanded && structure.children) {
for (const child of structure.children) {
const found = findClickedItem(child, y, currentY);
if (found) return found;
currentY += FOLDER_HEIGHT * countItems(item);
currentY += FOLDER_HEIGHT * (child.expanded ? countItems(child) : 1);
}
}
return null;
}
function countItems(item) {
return item.children ? item.children.reduce((sum, child) => sum + countItems(child), 1) : 1;
if (!item.expanded || !item.children) return 1;
return 1 + item.children.reduce((sum, child) => sum + countItems(child), 0);
}
function updateNodeSize() {