Implemented selection for RichTextPane.

Added reply and misc graphics.
This commit is contained in:
Snowyfox 2022-04-28 07:36:45 -04:00
parent 0590cc6c19
commit 39526a145f
12 changed files with 206 additions and 21 deletions

View File

@ -4,6 +4,7 @@ import javax.swing.JPanel;
import javax.swing.JMenuBar; import javax.swing.JMenuBar;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
@ -39,7 +40,6 @@ import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
class class
PostWindow extends JFrame PostWindow extends JFrame
implements ActionListener { implements ActionListener {
@ -125,6 +125,9 @@ implements ActionListener {
} }
else postDisplay.setMediaPreview(null); else postDisplay.setMediaPreview(null);
String html = post.get("content").value;
setTitle(TimelineComponent.textApproximation(html));
postDisplay.resetFocus(); postDisplay.resetFocus();
repaint(); repaint();
} }

View File

@ -6,20 +6,55 @@ import java.awt.FontMetrics;
import java.awt.Image; import java.awt.Image;
import java.awt.Color; import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Toolkit;
import java.util.List; import java.util.List;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.ListIterator; import java.util.ListIterator;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseEvent;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
class class
RichTextPane extends JComponent { RichTextPane extends JComponent
implements
MouseListener, MouseMotionListener, KeyListener,
Transferable, ClipboardOwner {
private List<Segment> private List<Segment>
text; text;
private int
selectionStart, selectionEnd;
// ---%-@-%--- // ---%-@-%---
public void public void
setText(List<Segment> text) { this.text = text; } setText(List<Segment> text)
{
this.text = text;
selectionStart = selectionEnd = -1;
}
public List<Segment>
getSelection()
{
List<Segment> returnee = new LinkedList<>();
if (selectionEnd == -1) return returnee;
if (selectionEnd < selectionStart) {
int t = selectionEnd;
selectionEnd = selectionStart;
selectionStart = t;
}
returnee.addAll(text.subList(selectionStart + 1, selectionEnd));
return returnee;
}
// - -%- - // - -%- -
@ -30,6 +65,7 @@ RichTextPane extends JComponent {
FontMetrics fm = g.getFontMetrics(getFont()); FontMetrics fm = g.getFontMetrics(getFont());
g.clearRect(0, 0, getWidth(), getHeight()); g.clearRect(0, 0, getWidth(), getHeight());
int o = 0;
for (Segment segment: text) for (Segment segment: text)
{ {
if (segment.image != null) { if (segment.image != null) {
@ -44,13 +80,134 @@ RichTextPane extends JComponent {
g.drawImage(img, x, y - h, w, h, this); g.drawImage(img, x, y - h, w, h, this);
continue; continue;
} }
if (o > selectionStart && o < selectionEnd)
{
int dx = fm.stringWidth(segment.text);
int dy1 = fm.getAscent();
int dy2 = dy1 + fm.getDescent();
g.setColor(new Color(0, 0, 0, 15));
g.fillRect(segment.x, segment.y - dy1, dx, dy2);
g.setColor(getForeground());
}
if (segment.link != null) g.setColor(Color.BLUE); if (segment.link != null) g.setColor(Color.BLUE);
g.drawString(segment.text, segment.x, segment.y); g.drawString(segment.text, segment.x, segment.y);
g.setColor(getForeground()); g.setColor(getForeground());
++o;
} }
} }
public void
mousePressed(MouseEvent eM)
{
requestFocusInWindow();
selectionStart = identify(eM.getX(), eM.getY()) - 2;
selectionEnd = -1;
repaint();
}
public void
mouseDragged(MouseEvent eM)
{
selectionEnd = identify(eM.getX(), eM.getY());
repaint();
}
private int
identify(int x, int y)
{
FontMetrics fm = getFontMetrics(getFont());
int iy = fm.getAscent(), lh = fm.getHeight();
y -= fm.getDescent();
if (y <= iy) y = iy;
else y += lh - ((y - iy) % lh);
/*
* Snaps y to the next baseline. Kind of obtuse,
* but it wasn't randomly derived, anyways
* you can test it for 13, 30, 47, etc.
*/
int o = 0;
for (Segment segment: text)
{
if (segment.y == y && segment.x > x) break;
if (segment.y > y) break;
++o;
}
return o;
}
public String
getTransferData(DataFlavor flavour)
{
assert flavour == DataFlavor.stringFlavor;
StringBuilder b = new StringBuilder();
for (Segment segment: getSelection())
{
if (segment.link != null) b.append(segment.link);
else if (segment.text != null) b.append(segment.text);
}
return b.toString();
}
public DataFlavor[]
getTransferDataFlavors()
{
return new DataFlavor[] { DataFlavor.stringFlavor };
/*
* We should probably also support javaJVMLocalObjectMimeType,
* so that the compose window can ask for the List<Segment>.
* Although also like, if we don't store emoji shortcodes in
* the image segments, there is no point. Anyways, what is
* important is the string format first, allowing us to
* copy links or large lengths of text.
*/
}
public boolean
isDataFlavorSupported(DataFlavor flavour)
{
return flavour == DataFlavor.stringFlavor;
}
public void
keyPressed(KeyEvent eK)
{
if (selectionEnd == -1) return;
if (eK.getKeyCode() != KeyEvent.VK_C) return;
if (!eK.isControlDown()) return;
Toolkit tk = Toolkit.getDefaultToolkit();
Clipboard cb = tk.getSystemClipboard();
cb.setContents(this, this);
}
public void
lostOwnership(Clipboard clipboard, Transferable contents) { }
public void
keyReleased(KeyEvent eK) { }
public void
keyTyped(KeyEvent eK) { }
public void
mouseClicked(MouseEvent eM) { }
public void
mouseReleased(MouseEvent eM) { }
public void
mouseEntered(MouseEvent eM) { }
public void
mouseExited(MouseEvent eM) { }
public void
mouseMoved(MouseEvent eM) { }
// - -%- - // - -%- -
public static List<Segment> public static List<Segment>
@ -272,6 +429,9 @@ RichTextPane extends JComponent {
RichTextPane() RichTextPane()
{ {
text = new LinkedList<>(); text = new LinkedList<>();
addMouseListener(this);
addMouseMotionListener(this);
addKeyListener(this);
} }
} }

View File

@ -127,17 +127,22 @@ implements ActionListener {
public void public void
connectionFailed(IOException eIo) connectionFailed(IOException eIo)
{ {
eIo.printStackTrace(); JOptionPane.showMessageDialog(
String s = eIo.getClass().getName(); TimelineWindow.this,
setTitle(s + " - JKomasto"); "Failed to fetch page.."
+ "\n" + eIo.getMessage()
);
} }
public void public void
requestFailed(int httpCode, Tree<String> json) requestFailed(int httpCode, Tree<String> json)
{ {
System.err.println(json.get("error").value); JOptionPane.showMessageDialog(
setTitle(httpCode + " - JKomasto"); TimelineWindow.this,
// lol... "Failed to fetch page.."
+ "\n" + json.get("error").value
+ "\n(HTTP code: " + httpCode + ")"
);
} }
public void public void
@ -174,14 +179,22 @@ implements ActionListener {
public void public void
connectionFailed(IOException eIo) connectionFailed(IOException eIo)
{ {
String s = eIo.getClass().getName(); JOptionPane.showMessageDialog(
setTitle(s + " - JKomasto"); TimelineWindow.this,
"Failed to fetch page.."
+ "\n" + eIo.getMessage()
);
} }
public void public void
requestFailed(int httpCode, Tree<String> json) requestFailed(int httpCode, Tree<String> json)
{ {
setTitle(httpCode + " - JKomasto"); JOptionPane.showMessageDialog(
TimelineWindow.this,
"Failed to fetch page.."
+ "\n" + json.get("error").value
+ "\n(HTTP code: " + httpCode + ")"
);
} }
public void public void
@ -224,14 +237,22 @@ implements ActionListener {
public void public void
connectionFailed(IOException eIo) connectionFailed(IOException eIo)
{ {
String s = eIo.getClass().getName(); JOptionPane.showMessageDialog(
setTitle(s + " - JKomasto"); TimelineWindow.this,
"Failed to fetch page.."
+ "\n" + eIo.getMessage()
);
} }
public void public void
requestFailed(int httpCode, Tree<String> json) requestFailed(int httpCode, Tree<String> json)
{ {
setTitle(httpCode + " - JKomasto"); JOptionPane.showMessageDialog(
TimelineWindow.this,
"Failed to fetch page.."
+ "\n" + json.get("error").value
+ "\n(HTTP code: " + httpCode + ")"
);
} }
public void public void
@ -623,7 +644,7 @@ implements
if (!cw.isEmpty()) if (!cw.isEmpty())
c.setBottom("(" + cw + ")"); c.setBottom("(" + cw + ")");
else else
c.setBottom(textApproximation(html) + " "); c.setBottom(textApproximation(html));
} }
for (int o = posts.size(); o < postPreviews.size(); ++o) for (int o = posts.size(); o < postPreviews.size(); ++o)
{ {
@ -786,6 +807,7 @@ implements
{ {
StringBuilder returnee = new StringBuilder(); StringBuilder returnee = new StringBuilder();
Tree<String> nodes = RudimentaryHTMLParser.depthlessRead(html); Tree<String> nodes = RudimentaryHTMLParser.depthlessRead(html);
if (nodes.size() == 0) return "-";
Tree<String> first = nodes.get(0); Tree<String> first = nodes.get(0);
for (Tree<String> node: nodes) for (Tree<String> node: nodes)
{ {

View File

@ -115,14 +115,14 @@ implements KeyListener, MouseListener, FocusListener {
if (isFocusOwner()) if (isFocusOwner())
g.drawImage(selectedOverlay, 0, 0, this); g.drawImage(selectedOverlay, 0, 0, this);
if (primaryToggled)
g.drawImage(primaryToggledIcon, 0, 0, this);
else
g.drawImage(primaryUntoggledIcon, 0, 0, this);
if (secondaryToggled) if (secondaryToggled)
g.drawImage(secondaryToggledIcon, 0, 0, this); g.drawImage(secondaryToggledIcon, 0, 0, this);
else else
g.drawImage(secondaryUntoggledIcon, 0, 0, this); g.drawImage(secondaryUntoggledIcon, 0, 0, this);
if (primaryToggled)
g.drawImage(primaryToggledIcon, 0, 0, this);
else
g.drawImage(primaryUntoggledIcon, 0, 0, this);
} }

BIN
graphics/miscToggled.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
graphics/miscUntoggled.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
graphics/replyToggled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
graphics/replyUntoggled.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB