Show and control channel subscriptions
Track the subscription state from the notifychannelsubscribed events, draw subscribed and unsubscribed channels apart with the pack's icons (and the built-in glyph as a hollow cone), and offer subscribe/unsubscribe entries — channel and family — in the channel context menu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,18 @@ public final class Icons {
|
||||
return IconTheme.icon(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pack's icon for the first key it has, or {@code null} when it has none
|
||||
* of them — packs differ in which variants they ship
|
||||
*/
|
||||
public static ImageIcon ofAny(String... keys) {
|
||||
for (String key : keys) {
|
||||
ImageIcon icon = IconTheme.icon(key);
|
||||
if (icon != null) return icon;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return the pack's icon at a given size, or {@code null} when it has none */
|
||||
public static ImageIcon of(String key, int size) {
|
||||
return IconTheme.icon(key, size);
|
||||
@@ -46,6 +58,18 @@ public final class Icons {
|
||||
return icon != null ? icon : make(fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #themed(String, Painter)}, but tries the keys in turn — packs need not
|
||||
* ship every variant (none of TeamSpeak's own has a subscribed password channel).
|
||||
*/
|
||||
private static ImageIcon themed(String[] keys, Painter fallback) {
|
||||
for (String key : keys) {
|
||||
ImageIcon icon = IconTheme.icon(key, SZ);
|
||||
if (icon != null) return icon;
|
||||
}
|
||||
return make(fallback);
|
||||
}
|
||||
|
||||
private static ImageIcon make(Painter p) {
|
||||
BufferedImage img = new BufferedImage(SZ, SZ, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g = img.createGraphics();
|
||||
@@ -62,18 +86,26 @@ public final class Icons {
|
||||
return themed("SERVER_GREEN", Icons::paintServer);
|
||||
}
|
||||
|
||||
public static ImageIcon channel() {
|
||||
return themed("CHANNEL_GREEN", g -> paintChannel(g, new Color(0x3E7CB1), false));
|
||||
public static ImageIcon channel(boolean subscribed) {
|
||||
return themed(keys("CHANNEL_GREEN", subscribed),
|
||||
g -> paintChannel(g, new Color(0x3E7CB1), false, subscribed));
|
||||
}
|
||||
|
||||
/** A channel nobody can join without its password. */
|
||||
public static ImageIcon channelLocked() {
|
||||
return themed("CHANNEL_PRIVATE", g -> paintChannel(g, new Color(0x8A6D3B), true));
|
||||
public static ImageIcon channelLocked(boolean subscribed) {
|
||||
return themed(keys("CHANNEL_PRIVATE", subscribed),
|
||||
g -> paintChannel(g, new Color(0x8A6D3B), true, subscribed));
|
||||
}
|
||||
|
||||
/** A channel that has reached its client limit. */
|
||||
public static ImageIcon channelFull() {
|
||||
return themed("CHANNEL_RED", g -> paintChannel(g, new Color(0xA53F3F), false));
|
||||
public static ImageIcon channelFull(boolean subscribed) {
|
||||
return themed(keys("CHANNEL_RED", subscribed),
|
||||
g -> paintChannel(g, new Color(0xA53F3F), false, subscribed));
|
||||
}
|
||||
|
||||
/** The pack keys to try for a channel icon, most specific first. */
|
||||
private static String[] keys(String base, boolean subscribed) {
|
||||
return subscribed ? new String[]{base + "_SUBSCRIBED", base} : new String[]{base};
|
||||
}
|
||||
|
||||
// ---- client status icons ----
|
||||
@@ -172,16 +204,24 @@ public final class Icons {
|
||||
g.fillOval(4, 10, 2, 2);
|
||||
}
|
||||
|
||||
private static void paintChannel(Graphics2D g, Color c, boolean lock) {
|
||||
g.setColor(c);
|
||||
g.setStroke(new BasicStroke(1.6f));
|
||||
// simple speaker-cone glyph
|
||||
g.fillRect(2, 6, 3, 4);
|
||||
/**
|
||||
* The speaker-cone glyph. An unsubscribed channel is drawn hollow and without the
|
||||
* sound waves, so the two states stay apart even without an icon pack.
|
||||
*/
|
||||
private static void paintChannel(Graphics2D g, Color c, boolean lock, boolean subscribed) {
|
||||
int[] xs = {5, 9, 9, 5};
|
||||
int[] ys = {6, 3, 13, 10};
|
||||
g.fillPolygon(xs, ys, 4);
|
||||
g.setStroke(new BasicStroke(1.4f));
|
||||
g.drawArc(9, 5, 4, 6, -60, 120);
|
||||
g.setColor(c);
|
||||
g.setStroke(new BasicStroke(subscribed ? 1.6f : 1.2f));
|
||||
if (subscribed) {
|
||||
g.fillRect(2, 6, 3, 4);
|
||||
g.fillPolygon(xs, ys, 4);
|
||||
g.setStroke(new BasicStroke(1.4f));
|
||||
g.drawArc(9, 5, 4, 6, -60, 120);
|
||||
} else {
|
||||
g.drawRect(2, 6, 3, 4);
|
||||
g.drawPolygon(xs, ys, 4);
|
||||
}
|
||||
if (lock) {
|
||||
g.setColor(new Color(0xB8860B));
|
||||
g.fillRoundRect(10, 9, 5, 5, 1, 1);
|
||||
|
||||
Reference in New Issue
Block a user