From f334153e6306e5198d2f02b7cba902a19cac8beb Mon Sep 17 00:00:00 2001 From: ericek111 Date: Fri, 14 Aug 2026 07:28:09 +0000 Subject: [PATCH] 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 --- .../java/com/ts3client/net/IconRepository.java | 3 +++ .../java/com/ts3client/ui/ServerTreePanel.java | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/ts3-client/core/src/main/java/com/ts3client/net/IconRepository.java b/ts3-client/core/src/main/java/com/ts3client/net/IconRepository.java index ad31867..286fd1c 100644 --- a/ts3-client/core/src/main/java/com/ts3client/net/IconRepository.java +++ b/ts3-client/core/src/main/java/com/ts3client/net/IconRepository.java @@ -118,6 +118,9 @@ public final class IconRepository { icons.put(id, data); writeDiskCache(id, data); if (onIconLoaded != null) onIconLoaded.run(); + } catch (IllegalStateException e) { + // Not connected (yet): a channel icon can be asked for while the tree is + // first painted, before file transfers are up. Leave it retryable. } catch (Exception e) { // Missing or permission-denied icons are common; don't ask again. failed.add(id); diff --git a/ts3-client/swing/src/main/java/com/ts3client/ui/ServerTreePanel.java b/ts3-client/swing/src/main/java/com/ts3client/ui/ServerTreePanel.java index 4c6c493..8a100ca 100644 --- a/ts3-client/swing/src/main/java/com/ts3client/ui/ServerTreePanel.java +++ b/ts3-client/swing/src/main/java/com/ts3client/ui/ServerTreePanel.java @@ -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;