JFormDesigner / FlatLaf

FlatLaf - Swing Look and Feel (with Darcula/IntelliJ themes support)
https://www.formdev.com/flatlaf/
Apache License 2.0
3.34k stars 266 forks source link

Making labels copyable #248

Closed Bios-Marcel closed 2 years ago

Bios-Marcel commented 3 years ago

I was wondering whether it'd be possible to make a labels text selectable and copyable. I've often had situations where I used a JTextArea or a JTextField and restyling them slightly so that they look like labels, but are still copyable. It'd be cool to be able to put a clientproperty on a label and be able to select and copy it's content.

public class FlatLabelUI extends BasicLabelUI

Is that even possible when we are still relying on the BaseLabelUI? I am assuming we have to draw the whole label manually then?

As to why I would want to do this. There is often information presented in a dialog or even in error dialogs, where it would be nice to be able to copy a certain text and send it to someone. Not sure if this is a nieche usecase, but I am kind of used to this from the web. In the web, everything is text and everything can be copied.

DevCharly commented 3 years ago

That should be possible.

Well, you need to register a mouse listener on the label and compute selection range somehow. E.g. double-click selects all, click-and-drag selects parts. Store the selection range in a client property of the label if label UI delegate is shared (only one instance for all labels). Or use fields for not shared label UI delegate.

Override FlatLabelUI.paintEnabledText() to paint the selection.

Copy selection somehow to clipboard. E.g. automatically as soon as selected. Or right-click via context menu.

But note that adding mouse listeners to labels has the side effect that the label catches mouse events if the mouse is located over the label. Without mouse listeners the parent of the label can receive mouse events.

If you use an label within another component, which needs to receive mouse events when clicked somewhere on it, including the label, you have a problem. E.g if you use a label in a tabbed pane as tab component, it is no longer possible to select the tab by clicking on tab title.

Bios-Marcel commented 3 years ago

Hm, I didn't consider that 🤔 Can't we both handle the event and forward it to the parent? Or would that have other side effects?

EDIT On second thought, that might be crazy too. But maybe it could be optional as well.

DevCharly commented 3 years ago

Can't we both handle the event and forward it to the parent?

Maybe, have not tried. Would require to go up the component hierarchy and find a component that handles mouse events and then dispatch the event somehow.

Or would that have other side effects?

Well, there is another side effect with mouse entered/exited events. When the label has mouse listeners and the mouse is moved over the label, Swing first sends an exited event to parent/ancestor and then an entered event to label. Without mouse listener on label, this does not happen.

Anyway there is another possibility to receive mouse events without side effects:

Toolkit.getDefaultToolkit().addAWTEventListener( e -> {
    System.out.println( e );
}, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK );

This listener receives all mouse events in all windows (before they are dispatched to components with mouse listeners). You can even consume the event (with e.consume()), which avoids dispatching.

Bios-Marcel commented 3 years ago

Hm 🤔 Couldn't that have performance issues when there are many labels? I am not particularly fit regarding the performance topic in swing. Or did you mean one global listener for all labels?

DevCharly commented 3 years ago

Sure, only one listener for all.

Poivin commented 3 years ago

This is an interesting feature but is it more a L&F feature or an application feature ?

DevCharly commented 2 years ago

Closing because do not plan to implement this in FlatLaf at the moment.

This could be implemented outside of FlatLaf by adding a global mouse listener with Toolkit.addAWTEventListener() to detect label selection and subclassing FlatLabelUI to paint label selection.