Initial commit: TS3J TeamSpeak 3 Java client

Swing desktop client (core/desktop/swing Maven modules) built on the
ts3j protocol library, included as a submodule. Native Opus voice with
voice-activation detection, push-to-talk, and audio pre-processing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 22:01:17 +00:00
commit 0f76258a05
48 changed files with 6347 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
package com.ts3client.ui;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Chat log with an input line and a target selector (current channel or whole
* server). Coloured styling mimics the TS3 chat pane.
*/
public final class ChatPanel extends JPanel {
/** Where an outgoing message should go. */
public enum Target {CHANNEL, SERVER}
public interface SendHandler {
void send(Target target, String text);
}
private final JTextPane log = new JTextPane();
private final JTextField input = new JTextField();
private final JComboBox<String> targetBox = new JComboBox<>(new String[]{"Channel", "Server"});
private final SimpleDateFormat time = new SimpleDateFormat("HH:mm:ss");
private SendHandler sendHandler;
public ChatPanel() {
super(new BorderLayout());
log.setEditable(false);
log.setBackground(Theme.CHAT_BG);
log.setFont(Theme.UI_FONT);
log.setBorder(BorderFactory.createEmptyBorder(4, 6, 4, 6));
JScrollPane scroll = new JScrollPane(log);
scroll.setBorder(BorderFactory.createLineBorder(new Color(0xD0D0D0)));
add(scroll, BorderLayout.CENTER);
JPanel bottom = new JPanel(new BorderLayout(4, 0));
bottom.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
targetBox.setPreferredSize(new Dimension(90, 24));
bottom.add(targetBox, BorderLayout.WEST);
bottom.add(input, BorderLayout.CENTER);
JButton send = new JButton("Send");
bottom.add(send, BorderLayout.EAST);
add(bottom, BorderLayout.SOUTH);
Runnable doSend = this::fireSend;
send.addActionListener(e -> doSend.run());
input.addActionListener(e -> doSend.run());
setInputEnabled(false);
}
public void setSendHandler(SendHandler h) {
this.sendHandler = h;
}
public void setInputEnabled(boolean enabled) {
input.setEnabled(enabled);
targetBox.setEnabled(enabled);
}
private void fireSend() {
String text = input.getText().trim();
if (text.isEmpty() || sendHandler == null) return;
Target t = targetBox.getSelectedIndex() == 1 ? Target.SERVER : Target.CHANNEL;
sendHandler.send(t, text);
input.setText("");
}
// ---- append helpers (safe from any thread) ----
public void appendSystem(String text) {
edt(() -> append("[" + time.format(new Date()) + "] ", Theme.CHAT_SYSTEM, false,
text, Theme.CHAT_SYSTEM, false));
}
public void appendMessage(String from, String text) {
edt(() -> {
append("[" + time.format(new Date()) + "] ", Theme.CHAT_SYSTEM, false, "", Theme.CHAT_SYSTEM, false);
append(from + ": ", Theme.CHAT_NAME, true, text, Theme.CHAT_TEXT, false);
});
}
private void edt(Runnable r) {
if (SwingUtilities.isEventDispatchThread()) r.run();
else SwingUtilities.invokeLater(r);
}
private void append(String prefix, Color prefixColor, boolean prefixBold,
String body, Color bodyColor, boolean bodyBold) {
StyledDocument doc = log.getStyledDocument();
try {
if (prefix != null && !prefix.isEmpty()) {
doc.insertString(doc.getLength(), prefix, style(prefixColor, prefixBold));
}
if (body != null && !body.isEmpty()) {
doc.insertString(doc.getLength(), body, style(bodyColor, bodyBold));
}
doc.insertString(doc.getLength(), "\n", style(bodyColor, false));
log.setCaretPosition(doc.getLength());
} catch (BadLocationException ignored) {
}
}
private static SimpleAttributeSet style(Color c, boolean bold) {
SimpleAttributeSet a = new SimpleAttributeSet();
StyleConstants.setForeground(a, c);
StyleConstants.setBold(a, bold);
StyleConstants.setFontFamily(a, "SansSerif");
StyleConstants.setFontSize(a, 12);
return a;
}
}