Repaint the whole tree viewport while scrolling

The right-aligned icon strip is positioned against the viewport rather than
the row, so the pixels a blit scroll reuses are stale and the icons leave
ghosts behind. Scroll the viewport in simple mode, and widen every repaint
request to the full visible width so a row whose label changes size cannot
leave its icons behind either.

Also stop blacklisting an icon whose download failed only because the file
transfer channel was not up yet: channel rows are painted during connect,
well before that point, so their icons would never be retried.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 07:28:09 +00:00
parent 610dce1bd9
commit f334153e63
2 changed files with 18 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.tree.DefaultMutableTreeNode;
@@ -96,6 +97,9 @@ public final class ServerTreePanel extends JScrollPane {
tree.setCellRenderer(new Renderer());
setViewportView(tree);
getViewport().setBackground(Theme.TREE_BG);
// The icon strip is drawn against the viewport's right edge, so the blitted
// pixels a scroll would reuse are stale; repaint the whole viewport instead.
getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);
// Within the tree a drag moves the client or channel; dropped elsewhere it
// yields the TS3 link BBCode, which the chat input accepts as plain text.
@@ -426,6 +430,17 @@ public final class ServerTreePanel extends JScrollPane {
super(model);
}
/**
* Widens every repaint request to the full visible width. Swing only asks for
* the row rectangle, which stops short of the right-aligned icon strip and would
* leave it behind when a row's label changes width.
*/
@Override
public void repaint(long tm, int x, int y, int width, int height) {
Rectangle visible = getVisibleRect();
super.repaint(tm, visible.x, y, visible.width, height);
}
void highlightChannel(TreePath path) {
if (path == highlight || (path != null && path.equals(highlight))) return;
highlight = path;