Show server and channel group icons in the tree
Download the icons a server advertises for its groups over the file transfer channel, cache them under the config directory, and draw them as a badge strip on each client's row, right-aligned against the tree's visible edge like the TeamSpeak client does. Groups without a custom icon fall back to the bundled default set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
101
ts3-client/swing/src/main/java/com/ts3client/ui/GroupIcons.java
Normal file
101
ts3-client/swing/src/main/java/com/ts3client/ui/GroupIcons.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package com.ts3client.ui;
|
||||
|
||||
import com.ts3client.net.Group;
|
||||
import com.ts3client.net.IconRepository;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Turns the {@link IconRepository}'s icon bytes into Swing icons for one server's
|
||||
* groups, caching the decoded images. Icons that still have to be downloaded simply
|
||||
* do not render yet; the repository repaints the view once they arrive.
|
||||
*/
|
||||
public final class GroupIcons {
|
||||
|
||||
/** Tree rows are 16×16 like TeamSpeak's own icon set. */
|
||||
private static final int SIZE = 16;
|
||||
|
||||
private final IconRepository repository;
|
||||
private final Map<Long, ImageIcon> decoded = new ConcurrentHashMap<>();
|
||||
|
||||
public GroupIcons(IconRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/** @return the group's icon, or {@code null} if it has none or it is not loaded yet */
|
||||
public ImageIcon iconOf(Group group) {
|
||||
if (group == null || group.iconId == 0) return null;
|
||||
return icon(group.iconId);
|
||||
}
|
||||
|
||||
public ImageIcon icon(long iconId) {
|
||||
ImageIcon cached = decoded.get(iconId);
|
||||
if (cached != null) return cached;
|
||||
|
||||
byte[] data = repository.get(iconId);
|
||||
if (data == null) return null;
|
||||
|
||||
ImageIcon icon = new ImageIcon(data);
|
||||
if (icon.getIconWidth() <= 0) return null;
|
||||
if (icon.getIconWidth() != SIZE || icon.getIconHeight() != SIZE) {
|
||||
icon = new ImageIcon(icon.getImage().getScaledInstance(SIZE, SIZE, Image.SCALE_SMOOTH));
|
||||
}
|
||||
decoded.put(iconId, icon);
|
||||
return icon;
|
||||
}
|
||||
|
||||
/** Collects the icons of a client's server groups plus its channel group, in display order. */
|
||||
public List<Icon> iconsOf(List<Group> serverGroups, Group channelGroup) {
|
||||
List<Icon> icons = new ArrayList<>();
|
||||
for (Group g : serverGroups) {
|
||||
ImageIcon icon = iconOf(g);
|
||||
if (icon != null) icons.add(icon);
|
||||
}
|
||||
ImageIcon channelIcon = iconOf(channelGroup);
|
||||
if (channelIcon != null) icons.add(channelIcon);
|
||||
return icons;
|
||||
}
|
||||
|
||||
/** Lays several icons out in a row, so a single tree cell can show a badge strip. */
|
||||
public static final class Row implements Icon {
|
||||
|
||||
private static final int GAP = 2;
|
||||
|
||||
private final List<Icon> icons;
|
||||
|
||||
public Row(List<Icon> icons) {
|
||||
this.icons = icons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
int offset = x;
|
||||
for (Icon icon : icons) {
|
||||
icon.paintIcon(c, g, offset, y + (getIconHeight() - icon.getIconHeight()) / 2);
|
||||
offset += icon.getIconWidth() + GAP;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
int width = 0;
|
||||
for (Icon icon : icons) width += icon.getIconWidth() + GAP;
|
||||
return Math.max(0, width - GAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
int height = 0;
|
||||
for (Icon icon : icons) height = Math.max(height, icon.getIconHeight());
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user