Fixed autoscroll, added flashing taskbar for new messages usernames now handled serverside

This commit is contained in:
Steven Tracey 2024-01-11 08:37:20 -06:00
parent 7a3b067648
commit 9d17a277b0
10 changed files with 217 additions and 9 deletions

View File

@ -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();

View File

@ -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 {
} ) } )
} }
} }

View File

@ -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 = "";
} }
} }

View File

@ -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) {

View 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);
}
}
}

View 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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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