Fixed autoscroll, added flashing taskbar for new messages usernames now handled serverside
This commit is contained in:
parent
7a3b067648
commit
9d17a277b0
@ -6,21 +6,34 @@ package tech.nevets.dlite;
|
|||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.io.IOException;
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.plaf.BorderUIResource;
|
import javax.swing.plaf.BorderUIResource;
|
||||||
|
|
||||||
import net.miginfocom.swing.*;
|
import net.miginfocom.swing.*;
|
||||||
|
import tech.nevets.dlite.components.SmartScroller;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author steven
|
* @author steven
|
||||||
*/
|
*/
|
||||||
public class ChatWindow extends JPanel {
|
public class ChatWindow extends JPanel {
|
||||||
|
private static ChatWindow instance;
|
||||||
|
|
||||||
private boolean connected = false;
|
private boolean connected = false;
|
||||||
private boolean darkModeBool = true;
|
private boolean darkModeBool = true;
|
||||||
|
|
||||||
public ChatWindow() {
|
public ChatWindow() {
|
||||||
initComponents();
|
initComponents();
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChatWindow getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetConnection() {
|
||||||
|
connected = false;
|
||||||
|
connStatus.setText("Disconnected: Socket Reset");
|
||||||
|
connectBtn.setText("Connect");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectBtnMouseClicked(MouseEvent e) {
|
private void connectBtnMouseClicked(MouseEvent e) {
|
||||||
@ -40,7 +53,7 @@ public class ChatWindow extends JPanel {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
chat.setText("");
|
chat.setText("");
|
||||||
conn.setComponents(chat, scrollPane);
|
conn.setComponents(chat);
|
||||||
connStatus.setText("Connected");
|
connStatus.setText("Connected");
|
||||||
connectBtn.setText("Disconnect");
|
connectBtn.setText("Disconnect");
|
||||||
connected = true;
|
connected = true;
|
||||||
@ -111,6 +124,7 @@ public class ChatWindow extends JPanel {
|
|||||||
connectBtn = new JButton();
|
connectBtn = new JButton();
|
||||||
scrollPane = new JScrollPane();
|
scrollPane = new JScrollPane();
|
||||||
chat = new JTextArea();
|
chat = new JTextArea();
|
||||||
|
new SmartScroller(scrollPane, SmartScroller.VERTICAL, SmartScroller.END);
|
||||||
messageBox = new JTextField();
|
messageBox = new JTextField();
|
||||||
sendBtn = new JButton();
|
sendBtn = new JButton();
|
||||||
|
|
||||||
|
@ -57,6 +57,9 @@ new FormModel {
|
|||||||
"editable": false
|
"editable": false
|
||||||
"font": new java.awt.Font( "Tahoma", 0, 11 )
|
"font": new java.awt.Font( "Tahoma", 0, 11 )
|
||||||
"lineWrap": true
|
"lineWrap": true
|
||||||
|
auxiliary() {
|
||||||
|
"JavaCodeGenerator.postCreateCode": "new SmartScroller(scrollPane, SmartScroller.VERTICAL, SmartScroller.END);"
|
||||||
|
}
|
||||||
} )
|
} )
|
||||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||||
"value": "cell 0 2 3 1,grow"
|
"value": "cell 0 2 3 1,grow"
|
||||||
@ -81,4 +84,3 @@ new FormModel {
|
|||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ public class Connection {
|
|||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
private JTextArea chat;
|
private JTextArea chat;
|
||||||
private JScrollBar scrollBar;
|
|
||||||
|
|
||||||
public Connection(String username, String address) throws Exception {
|
public Connection(String username, String address) throws Exception {
|
||||||
String[] addrSplit = address.split(":");
|
String[] addrSplit = address.split(":");
|
||||||
@ -46,16 +45,16 @@ public class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessage(String message) {
|
public void sendMessage(String message) {
|
||||||
out.println(username + "> " + message);
|
out.println(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setComponents(JTextArea chat, JScrollPane scrollBar) {
|
public void setComponents(JTextArea chat) {
|
||||||
this.chat = chat;
|
this.chat = chat;
|
||||||
this.scrollBar = scrollBar.getVerticalScrollBar();
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
is.transferTo(new ChatWriter());
|
is.transferTo(new ChatWriter());
|
||||||
} catch (IOException ignored) {}
|
} catch (IOException ignored) {}
|
||||||
|
close();
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +64,7 @@ public class Connection {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
ChatWindow.getInstance().resetConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ChatWriter extends OutputStream {
|
private class ChatWriter extends OutputStream {
|
||||||
@ -79,7 +79,8 @@ public class Connection {
|
|||||||
@Override
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
chat.append(buf);
|
chat.append(buf);
|
||||||
scrollBar.setValue(scrollBar.getMaximum());
|
Notifier.messageReceived(Main.getFrame());
|
||||||
|
//Notifier.sendNotification(buf.substring(0, buf.indexOf(">") - 1), buf.substring(buf.indexOf(">")));
|
||||||
buf = "";
|
buf = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public class Main {
|
|||||||
ChatWindow window = new ChatWindow();
|
ChatWindow window = new ChatWindow();
|
||||||
frame = new JFrame();
|
frame = new JFrame();
|
||||||
frame.add(window);
|
frame.add(window);
|
||||||
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
frame.pack();
|
frame.pack();
|
||||||
frame.setTitle("Discord Lite");
|
frame.setTitle("Discord Lite");
|
||||||
frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Main.class.getResource("/icon.png")));
|
frame.setIconImage(Toolkit.getDefaultToolkit().createImage(Main.class.getResource("/icon.png")));
|
||||||
@ -31,6 +31,10 @@ public class Main {
|
|||||||
frame.setVisible(true);
|
frame.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static JFrame getFrame() {
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setDarkMode(boolean darkMode) {
|
public static void setDarkMode(boolean darkMode) {
|
||||||
try {
|
try {
|
||||||
if (darkMode) {
|
if (darkMode) {
|
||||||
|
13
src/main/java/tech/nevets/dlite/Notifier.java
Normal file
13
src/main/java/tech/nevets/dlite/Notifier.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package tech.nevets.dlite;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class Notifier {
|
||||||
|
|
||||||
|
public static void messageReceived(JFrame frame) {
|
||||||
|
if (!frame.isFocused()) {
|
||||||
|
Taskbar.getTaskbar().requestWindowUserAttention(frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
174
src/main/java/tech/nevets/dlite/components/SmartScroller.java
Normal file
174
src/main/java/tech/nevets/dlite/components/SmartScroller.java
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
package tech.nevets.dlite.components;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.text.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SmartScroller will attempt to keep the viewport positioned based on
|
||||||
|
* the users interaction with the scrollbar. The normal behaviour is to keep
|
||||||
|
* the viewport positioned to see new data as it is dynamically added.
|
||||||
|
*
|
||||||
|
* Assuming vertical scrolling and data is added to the bottom:
|
||||||
|
*
|
||||||
|
* - when the viewport is at the bottom and new data is added,
|
||||||
|
* then automatically scroll the viewport to the bottom
|
||||||
|
* - when the viewport is not at the bottom and new data is added,
|
||||||
|
* then do nothing with the viewport
|
||||||
|
*
|
||||||
|
* Assuming vertical scrolling and data is added to the top:
|
||||||
|
*
|
||||||
|
* - when the viewport is at the top and new data is added,
|
||||||
|
* then do nothing with the viewport
|
||||||
|
* - when the viewport is not at the top and new data is added, then adjust
|
||||||
|
* the viewport to the relative position it was at before the data was added
|
||||||
|
*
|
||||||
|
* Similiar logic would apply for horizontal scrolling.
|
||||||
|
*/
|
||||||
|
public class SmartScroller implements AdjustmentListener
|
||||||
|
{
|
||||||
|
public final static int HORIZONTAL = 0;
|
||||||
|
public final static int VERTICAL = 1;
|
||||||
|
|
||||||
|
public final static int START = 0;
|
||||||
|
public final static int END = 1;
|
||||||
|
|
||||||
|
private int viewportPosition;
|
||||||
|
|
||||||
|
private JScrollBar scrollBar;
|
||||||
|
private boolean adjustScrollBar = true;
|
||||||
|
|
||||||
|
private int previousValue = -1;
|
||||||
|
private int previousMaximum = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience constructor.
|
||||||
|
* Scroll direction is VERTICAL and viewport position is at the END.
|
||||||
|
*
|
||||||
|
* @param scrollPane the scroll pane to monitor
|
||||||
|
*/
|
||||||
|
public SmartScroller(JScrollPane scrollPane)
|
||||||
|
{
|
||||||
|
this(scrollPane, VERTICAL, END);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience constructor.
|
||||||
|
* Scroll direction is VERTICAL.
|
||||||
|
*
|
||||||
|
* @param scrollPane the scroll pane to monitor
|
||||||
|
* @param viewportPosition valid values are START and END
|
||||||
|
*/
|
||||||
|
public SmartScroller(JScrollPane scrollPane, int viewportPosition)
|
||||||
|
{
|
||||||
|
this(scrollPane, VERTICAL, viewportPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify how the SmartScroller will function.
|
||||||
|
*
|
||||||
|
* @param scrollPane the scroll pane to monitor
|
||||||
|
* @param scrollDirection indicates which JScrollBar to monitor.
|
||||||
|
* Valid values are HORIZONTAL and VERTICAL.
|
||||||
|
* @param viewportPosition indicates where the viewport will normally be
|
||||||
|
* positioned as data is added.
|
||||||
|
* Valid values are START and END
|
||||||
|
*/
|
||||||
|
public SmartScroller(JScrollPane scrollPane, int scrollDirection, int viewportPosition)
|
||||||
|
{
|
||||||
|
if (scrollDirection != HORIZONTAL
|
||||||
|
&& scrollDirection != VERTICAL)
|
||||||
|
throw new IllegalArgumentException("invalid scroll direction specified");
|
||||||
|
|
||||||
|
if (viewportPosition != START
|
||||||
|
&& viewportPosition != END)
|
||||||
|
throw new IllegalArgumentException("invalid viewport position specified");
|
||||||
|
|
||||||
|
this.viewportPosition = viewportPosition;
|
||||||
|
|
||||||
|
if (scrollDirection == HORIZONTAL)
|
||||||
|
scrollBar = scrollPane.getHorizontalScrollBar();
|
||||||
|
else
|
||||||
|
scrollBar = scrollPane.getVerticalScrollBar();
|
||||||
|
|
||||||
|
scrollBar.addAdjustmentListener( this );
|
||||||
|
|
||||||
|
// Turn off automatic scrolling for text components
|
||||||
|
|
||||||
|
Component view = scrollPane.getViewport().getView();
|
||||||
|
|
||||||
|
if (view instanceof JTextComponent)
|
||||||
|
{
|
||||||
|
JTextComponent textComponent = (JTextComponent)view;
|
||||||
|
DefaultCaret caret = (DefaultCaret)textComponent.getCaret();
|
||||||
|
caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void adjustmentValueChanged(final AdjustmentEvent e)
|
||||||
|
{
|
||||||
|
SwingUtilities.invokeLater(new Runnable()
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
checkScrollBar(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Analyze every adjustment event to determine when the viewport
|
||||||
|
* needs to be repositioned.
|
||||||
|
*/
|
||||||
|
private void checkScrollBar(AdjustmentEvent e)
|
||||||
|
{
|
||||||
|
// The scroll bar listModel contains information needed to determine
|
||||||
|
// whether the viewport should be repositioned or not.
|
||||||
|
|
||||||
|
JScrollBar scrollBar = (JScrollBar)e.getSource();
|
||||||
|
BoundedRangeModel listModel = scrollBar.getModel();
|
||||||
|
int value = listModel.getValue();
|
||||||
|
int extent = listModel.getExtent();
|
||||||
|
int maximum = listModel.getMaximum();
|
||||||
|
|
||||||
|
boolean valueChanged = previousValue != value;
|
||||||
|
boolean maximumChanged = previousMaximum != maximum;
|
||||||
|
|
||||||
|
// Check if the user has manually repositioned the scrollbar
|
||||||
|
|
||||||
|
if (valueChanged && !maximumChanged)
|
||||||
|
{
|
||||||
|
if (viewportPosition == START)
|
||||||
|
adjustScrollBar = value != 0;
|
||||||
|
else
|
||||||
|
adjustScrollBar = value + extent >= maximum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the "value" so we can reposition the viewport and
|
||||||
|
// distinguish between a user scroll and a program scroll.
|
||||||
|
// (ie. valueChanged will be false on a program scroll)
|
||||||
|
|
||||||
|
if (adjustScrollBar && viewportPosition == END)
|
||||||
|
{
|
||||||
|
// Scroll the viewport to the end.
|
||||||
|
scrollBar.removeAdjustmentListener( this );
|
||||||
|
value = maximum - extent;
|
||||||
|
scrollBar.setValue( value );
|
||||||
|
scrollBar.addAdjustmentListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adjustScrollBar && viewportPosition == START)
|
||||||
|
{
|
||||||
|
// Keep the viewport at the same relative viewportPosition
|
||||||
|
scrollBar.removeAdjustmentListener( this );
|
||||||
|
value = value + maximum - previousMaximum;
|
||||||
|
scrollBar.setValue( value );
|
||||||
|
scrollBar.addAdjustmentListener( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
previousValue = value;
|
||||||
|
previousMaximum = maximum;
|
||||||
|
}
|
||||||
|
}
|
BIN
src/main/resources/icon-old.ico
Normal file
BIN
src/main/resources/icon-old.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
src/main/resources/icon-old.png
Normal file
BIN
src/main/resources/icon-old.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 8.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Loading…
Reference in New Issue
Block a user