Some browsers show for HTML Textareas a handle to resize the textbox. Is there any way to react to this resize events in GWT?
I tried it with this code, but the event is not triggered:
TextArea textArea = new TextArea();
textArea.addHandler(new ResizeHandler() {
#Override
public void onResize(ResizeEvent event) {
System.out.println("resize");
}
}, ResizeEvent.getType());
"It doesn't look like you can specifically attach events to resizing a textarea. The resize event fires when the window is resized."
https://stackoverflow.com/a/2096352/1467482
The question already is two years old, but for them who are brought by Google to this question I have a solution.
You can use the mouse-down, mouse-move and mouse-up events to react on resizing of the text area. Here is a skeleton code:
TextArea textArea = new TextArea();
...
textArea.addMouseDownHandler(new MouseDownHandler() {
private HandlerRegistration mouseMoveUpRegistration;
private int lastWidth;
private int lastHeight;
#Override
public void onMouseDown(MouseDownEvent event) {
lastWidth = getOffsetWidth();
lastHeight = getOffsetHeight();
if (mouseMoveUpRegistration == null) {
mouseMoveUpRegistration = Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getTypeInt() == Event.ONMOUSEMOVE || event.getTypeInt() == Event.ONMOUSEUP) {
int width = getOffsetWidth();
int height = getOffsetHeight();
if (width != lastWidth || height != lastHeight) {
// do whatever you want to to while still resizing
lastWidth = width;
lastHeight = height;
}
if (event.getTypeInt() == Event.ONMOUSEUP) {
// do whatever you want to do when resizing finished
// perhaps check actual and initial size to see if the size really changed
/* mouse up => additionally remove the handler */
if (mouseMoveUpRegistration != null) {
mouseMoveUpRegistration.removeHandler();
mouseMoveUpRegistration = null;
}
}
}
});
}
}
});
Related
In JavaFX's TableView (and TreeTableView) it's really hard to reorder columns using drag & drop when the horizontal scrollbar is present, because the table doesn't scroll automatically when one want's to drag the column to the currently not visible (off the scroll pane viewport) position.
I've noticed that there are already a bug (enhancement) reports for this:
https://bugs.openjdk.java.net/browse/JDK-8092314
https://bugs.openjdk.java.net/browse/JDK-8092355
https://bugs.openjdk.java.net/browse/JDK-8213739
... but as it haven't been tackled for quite some time I am wondering whether there is any other way to achieve the same behavior using the current API.
There is SSCCE:
public class TableViewColumnReorderDragSSCCE extends Application {
public static final int NUMBER_OF_COLUMNS = 30;
public static final int MAX_WINDOW_WIDTH = 480;
#Override
public void start(Stage stage) {
stage.setScene(new Scene(createTable()));
stage.show();
stage.setMaxWidth(MAX_WINDOW_WIDTH);
}
private TableView<List<String>> createTable() {
final TableView<List<String>> tableView = new TableView<>();
initColumns(tableView);
return tableView;
}
private void initColumns(TableView<List<String>> tableView) {
for (int i=0; i<NUMBER_OF_COLUMNS; i++) {
tableView.getColumns().add(new TableColumn<>("Column " + i));
}
tableView.getItems().add(Collections.emptyList());
}
}
Steps to reproduce:
Run the above SSCCE
Try to drag Column 0 after Column 29
I am after a fully functional solution (if any).
As no complete solution was provided I've came up with one of my own. I've introduced a (ColumnsOrderingEnhancer) implementation which will enhance the table view columns reordering by automatic scrolling (when needed).
Usage (with the table view defined in the above SSCCE):
// Enhance table view columns reordering
final ColumnsOrderingEnhancer<List<String>> columnsOrderingEnhancer = new ColumnsOrderingEnhancer<>(tableView);
columnsOrderingEnhancer.init();
ColumnsOrderingEnhancer implementation:
public class ColumnsOrderingEnhancer<T> {
private final TableView<T> tableView;
public ColumnsOrderingEnhancer(TableView<T> tableView) {
this.tableView = tableView;
}
public void init() {
tableView.skinProperty().addListener((observable, oldSkin, newSkin) -> {
// This can be done only when skin is ready
final TableHeaderRow header = (TableHeaderRow) tableView.lookup("TableHeaderRow");
final MouseDraggingDirectionHelper mouseDraggingDirectionHelper = new MouseDraggingDirectionHelper(header);
final ScrollBar horizontalScrollBar = getTableViewHorizontalScrollbar();
// This is the most important part which is responsible for scrolling table during the column dragging out of the viewport.
header.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> {
final double totalHeaderWidth = header.getWidth();
final double xMousePosition = event.getX();
final MouseDraggingDirectionHelper.Direction direction = mouseDraggingDirectionHelper.getLastDirection();
maybeChangeScrollBarPosition(horizontalScrollBar, totalHeaderWidth, xMousePosition, direction);
});
});
}
private void maybeChangeScrollBarPosition(ScrollBar horizontalScrollBar, double totalHeaderWidth, double xMousePosition, MouseDraggingDirectionHelper.Direction direction) {
if (xMousePosition > totalHeaderWidth && direction == RIGHT) {
horizontalScrollBar.increment();
}
else if (xMousePosition < 0 && direction == LEFT) {
horizontalScrollBar.decrement();
}
}
private ScrollBar getTableViewHorizontalScrollbar() {
Set<Node> scrollBars = tableView.lookupAll(".scroll-bar");
final Optional<Node> horizontalScrollBar =
scrollBars.stream().filter(node -> ((ScrollBar) node).getOrientation().equals(Orientation.HORIZONTAL)).findAny();
try {
return (ScrollBar) horizontalScrollBar.get();
}
catch (NoSuchElementException e) {
return null;
}
}
/**
* A simple class responsible for determining horizontal direction of the mouse during dragging phase.
*/
static class MouseDraggingDirectionHelper {
private double xLastMousePosition = -1;
private Direction direction = null;
MouseDraggingDirectionHelper(Node node) {
// Event filters that are determining when scrollbar needs to be incremented/decremented
node.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> xLastMousePosition = event.getX());
node.addEventFilter(MouseEvent.MOUSE_DRAGGED, event -> {
direction = ((event.getX() - xLastMousePosition > 0) ? RIGHT : LEFT);
xLastMousePosition = event.getX();
});
}
enum Direction {
LEFT,
RIGHT
}
public Direction getLastDirection() {
return direction;
}
}
}
End result (which works surprisingly well):
It's not impossible to work around. You could start with something like this, though it is a very crude implementation, I'm sure in principle it can be refined to something reasonable:
tableView.setOnMouseExited(me -> {
if (me.isPrimaryButtonDown()) { // must be dragging
Bounds tvBounds = tableView.getBoundsInLocal();
double x = me.getX();
if (x < tvBounds.getMinX()) {
// Scroll to the left
tableView.scrollToColumnIndex(0);
} else if (x > tvBounds.getMaxX()) {
// Scroll to the right
tableView.scrollToColumnIndex(tableView.getColumns().size()-1);
}
}
});
In a proper implementation you would likely have to sneak around the Node hierarchy and find the width of the table columns and determine what the next out-of-view column is so you can scroll to the exact right column. Remember when you did that so you can do it again if the user continues to drag outside the table, but not too fast.
EDIT: Based on your self-answer, here is my take on it. I've compacted your code a bit and made it work on JavaFX 8.0:
static class TableHeaderScroller implements EventHandler<MouseEvent> {
private TableView tv;
private Pane header;
private ScrollBar scrollBar;
private double lastX;
public static void install(TableView tv) {
TableHeaderScroller ths = new TableHeaderScroller(tv);
tv.skinProperty().addListener(ths::skinListener);
}
private TableHeaderScroller(TableView tv) {
this.tv = tv;
}
private void skinListener(ObservableValue<? extends Skin<?>> observable, Skin<?> oldValue, Skin<?> newValue) {
if (header != null) {
header.removeEventFilter(MouseEvent.MOUSE_DRAGGED, this);
}
header = (Pane) tv.lookup("TableHeaderRow");
if (header != null) {
tv.lookupAll(".scroll-bar").stream().map(ScrollBar.class::cast)
.filter(sb -> sb.getOrientation() == Orientation.HORIZONTAL)
.findFirst().ifPresent( sb -> {
TableHeaderScroller.this.scrollBar = sb;
header.addEventFilter(MouseEvent.MOUSE_DRAGGED, TableHeaderScroller.this);
});
}
}
#Override
public void handle(MouseEvent event) {
double x = event.getX();
double sign = Math.signum(x - lastX);
lastX = x;
int dir = x < 0 ? -1 : x > header.getWidth() ? 1 : 0;
if (dir != 0 && dir == sign) {
if (dir < 0) {
scrollBar.decrement();
} else {
scrollBar.increment();
}
}
}
}
I'm doing a school project for a chess game and I'm currently stuck at the DnD operation of the pieces.
In the code, I passed the TransferHandler.MOVE parameter in exportAsDrag() to make it a MOVE operation. However, the behavior of the TransferHandler is still COPY instead of MOVE, when dragging and dropping icon from JLabels.
I tried setting the icon of the source JLabel to null in exportDone() in TransferHandler anonymous class but the icon will disappear if the source and destination of the DnD operation is the same. If there is any more methods I should override/add or any other way to accomplish the same thing, please let me know about it.
MouseListener listener = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
ChessTiles c = (ChessTiles) e.getSource();
TransferHandler handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.MOVE)
}
};
private static TransferHandler handler = new TransferHandler("icon")
{
#Override
public int getSourceActions (JComponent c)
{
return MOVE;
}
};
tileArray[x][y].addMouseListener(listener);
tileArray[x][y].setTransferHandler(handler);
Consider the documentation of TransferHandler.exportDone:
Invoked after data has been exported. This method should remove the data that was transferred if the action was MOVE.
This should answer both question. First, you are indeed responsible to implement the move semantic and second, you should only do it when the action has the value MOVE. Besides the possibility of other transfer types, which doesn’t apply to your scenario, as you do not support them, it may get invoked with a zero action, to allow cleanup after an aborted transfer. This may even happen right from the exportAsDrag method when the preconditions were not met.
If you do not want to support dragging onto itself, you may disable the drop target temporarily, using the exportDone method for resetting the property.
E.g.
public class DragAndDropExample {
public static void main(String[] args) {
EventQueue.invokeLater(DragAndDropExample::init);
}
private static void init() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(ReflectiveOperationException|UnsupportedLookAndFeelException ex) {}
try {
BufferedImage img = ImageIO.read(
new URL("https://cdn.sstatic.net/img/favicons-sprite32.png"));
img = img.getSubimage(0, 11844, 32, 32);
ICON = new ImageIcon(img);
} catch(IOException ex) {
ICON = UIManager.getIcon("OptionPane.errorIcon");
}
JFrame frame = new JFrame("Test");
Container c = frame.getContentPane();
final int gridWidth = 4, gridHeight = 4;
c.setLayout(new GridLayout(gridHeight, gridWidth, 4, 4));
for(int y = 0; y < gridHeight; y++) {
for(int x = 0; x < gridWidth; x++) {
create(x, y, c);
}
}
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
static Icon ICON;
static final MouseAdapter DRAG_INIT = new MouseAdapter() {
#Override public void mousePressed(MouseEvent e) {
var c = (JComponent) e.getSource();
var handler = c.getTransferHandler();
handler.exportAsDrag(c, e, TransferHandler.MOVE);
}
};
static final TransferHandler ICON_TRANSFER = new TransferHandler( "icon" ) {
#Override public void exportAsDrag(JComponent comp, InputEvent e, int action) {
comp.getDropTarget().setActive(false);
super.exportAsDrag(comp, e, action);
}
#Override public int getSourceActions(JComponent c) {
return MOVE;
}
#Override protected void exportDone(
JComponent source, Transferable data, int action) {
source.getDropTarget().setActive(true);
if (action == MOVE) {
((JLabel)source).setIcon(null);
}
}
};
private static void create(int x, int y, Container c) {
JLabel l = new JLabel("\u00a0");
if(x == 0 && y == 0) l.setIcon(ICON);
l.setBorder(BorderFactory.createLineBorder(Color.lightGray, 1));
l.setTransferHandler(ICON_TRANSFER);
l.addMouseListener(DRAG_INIT);
c.add(l);
}
}
If you do not want to disable it, you may store the component, to check whether source and target are the same like in this answer, but you should set the remembered component to null in the exportDone method, to ensure that there are no memory leaks.
I tried setting the icon of the source JLabel to null in exportDone() in TransferHandler anonymous class but the icon will disappear if the source and destination of the DnD operation is the same.
Yes, that needs to be done.
Also, by overriding the importData(...) method you can save the "target" component so you can check if the source/target are the same component:
TransferHandler iconHandler = new TransferHandler( "icon" )
{
Component target;
#Override
public int getSourceActions(JComponent c)
{
return MOVE;
}
#Override
public boolean importData(TransferSupport info)
{
target = info.getComponent();
return super.importData( info );
}
#Override
protected void exportDone(JComponent source, Transferable data, int action)
{
if (action == MOVE
&& source != target)
{
((JLabel)source).setIcon(null);
}
}
};
Someone suggested that I override the exportDone(...) function of TransferHandler like so,
#Override
protected void exportDone(JComponent source, Transferable data, int action)
{
if (action == MOVE)
{
((JLabel)source).setIcon(null);
}
//((JLabel)source).setIcon(null);
}
Without the if the statement, if I set the icon to null, the icon will disappear, regardless of the bool returned by importData(...). With it, the icon stays if the returned value of importData(...) is false. So is it safe to assume that after importData(...) is called, then only exportDone(...) is called?
It works but now I'm curious about the sequence of TransferHandler's internal function calls, after handler.exportAsDrag(...) is invoked.
The texts are inside JPanels and they are all inside a JScrollPane. I have set to catch the MouseWheelEvent (scrolling) so when the Ctrl key is pressed, and mouse wheel is turned, the texts would zoom in/out (font size is increased/decreased). This has lead to not being able to scroll through the list of fonts anymore unless I point the mouse cursor directly on the scrollbar. I want to know if there is a way to return the event when the Ctrl key is not held down. Is that possible? I looked through the MouseWheelEvent api and didn't find anything useful. Googling didn't give me anything either.
If I did understood you correctly the answer you are looking for should be like this the main idea to create to different mouse wheel event for your form and your scrollpane. Watch the entire animation you will see your problem is fixed , you can make your manipulations based on this idea
you should add below to your fields ;
public int defaultTextSize = 12;
boolean ctrlcheck =false;
this is jLabel4 to display CTRL is not pressed in constructor
jLabel4.setText("CTRL IS NOT PRESSED");
your scrollpane mouse event should be like this
private void jScrollPane1MouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
int notches = evt.getWheelRotation();
if (notches < 0) {
if (ctrlcheck == true) {
defaultTextSize = defaultTextSize + 3;
Font zoomfont = new Font("Monospaced", Font.PLAIN, defaultTextSize);
jLabel1.setFont(zoomfont);
jLabel2.setFont(zoomfont);
jLabel3.setFont(zoomfont);
jLabel1.repaint();
jLabel2.repaint();
jLabel3.repaint();
} else {
System.out.println("CTRL IS NOT PRESSED");
}
} else {
if (ctrlcheck == true) {
defaultTextSize = defaultTextSize - 3;
Font zoomfont2 = new Font("Monospaced", Font.ITALIC, defaultTextSize);
jLabel1.setFont(zoomfont2);
jLabel2.setFont(zoomfont2);
jLabel3.setFont(zoomfont2);
jLabel1.repaint();
jLabel2.repaint();
jLabel3.repaint();
}
}
}
You need FORM Key Released Event Like this
private void formKeyReleased(java.awt.event.KeyEvent evt) {
jLabel4.setText("CTRL IS NOT PRESSED");
ctrlcheck = false;
}
You need separate event for formMouseWheelMoved
private void formMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
int notches = evt.getWheelRotation();
if (notches < 0) {
if (ctrlcheck == true) {
defaultTextSize = defaultTextSize + 3;
Font zoomfont = new Font("Monospaced", Font.PLAIN, defaultTextSize);
jLabel1.setFont(zoomfont);
jLabel2.setFont(zoomfont);
jLabel3.setFont(zoomfont);
jLabel1.repaint();
jLabel2.repaint();
jLabel3.repaint();
} else {
System.out.println("CTRL IS NOT PRESSED");
}
} else {
if (ctrlcheck == true) {
defaultTextSize = defaultTextSize - 3;
Font zoomfont2 = new Font("Monospaced", Font.ITALIC, defaultTextSize);
jLabel1.setFont(zoomfont2);
jLabel2.setFont(zoomfont2);
jLabel3.setFont(zoomfont2);
jLabel1.repaint();
jLabel2.repaint();
jLabel3.repaint();
}
}
}
You need Form Keypressed Event as well to check key is still pressed
private void formKeyPressed(java.awt.event.KeyEvent evt) {
int key = evt.getKeyCode();
if (key == KeyEvent.VK_CONTROL) {
jLabel4.setText("CTRL IS PRESSED");
ctrlcheck = true;
}
}
I used #MadProgrammer 's comment and got some idea from #okrman 's answer and came up with a better way of doing this. In my solution, I don't need to add two separate event listeners, namely, one to the JLabel and the other one to the JScrollPane. I use only one event listener on the JLabel. The main key here is Component#dispatchEvent as mentioned by #MadProgrammer. I just found (rather remembered from my foggy memory) an excellent way to get a hold of the parent JScrollPane inside JLabel.
Of course, the example I have given here is just an example. I have not set the widths or heights for any of the components. The soul purpose of this example is just to show how the problem is solved.
public class ParentScrollPane extends JScrollPane {
public ParentScrollPane() {
JPanel textContainer = new JPanel();
// I have multiple labels in a for loop, this is
// just an example
JLabel sLabel = new JLabel("SOME LABEL TEXT");
sLabel.setParent(thisPane);
textContainer.add(sLabel);
setViewportView(textContainer);
}
// this interface is important and key to the solution
public interface ParentSetter {
public void setParent(Component p);
}
private JScrollPane thisPane;
}
public class CustomLabel extends JLabel implements ParentScrollPane.ParentSetter {
public CustomLabel(String text) {
super(text);
// one event listener is suffice
addMouseWheelListener(new MouseWheelListener() {
#Override
public void mouseWheelMoved(MouseWheelEvent e) {
// if the Ctrl key is down
if (e.isControlDown()) {
// then zoom the font in the JLabel
} else {
// then scroll the parent scroll pane
labelParent.dispatchEvent(e);
}
}
});
}
// pay very close attention to this and
// how the parent scroll pane is set here
#Override
public void setParent(Component lParent) {
labelParent = lParent;
}
private Component labelParent;
}
I have a Class which displays a label. When I hover the mouse over that label there is a empty tooltip being displayed. I would want to remove that tooltip(I mean I dont want it to be shown). I can easily say tooltip.setVisibility(false) but I should not change the code inside the MouseTrackListener Anonymous class. I need to use a toltip property pr anythin else so that when I extend this class I need to have an option to easily set the visibility of this tooltip if needed or disable it if not needed.
Here is my Code snippet (JAVA SWT)
tooltip = new ToolTip(parent.getShell(), SWT.NONE);
MouseTrackListener mouseTrackListener = new MouseTrackListener() {
#Override
public void mouseEnter(MouseEvent e) {
if (text != null && !text.isEmpty()) {
tooltip.setLocation(Display.getCurrent().getCursorLocation().x,
Display.getCurrent().getCursorLocation().y + TOOLTIP_OFFSET_Y);
tooltip.setVisible(true);
}
}
#Override
public void mouseExit(MouseEvent e) {
if (text != null && !text.isEmpty()) {
tooltip.setVisible(false);
}
}
#Override
public void mouseHover(MouseEvent e) {
}};
label.addMouseTrackListener(mouseTrackListener);
iconLabel.addMouseTrackListener(mouseTrackListener);
Not quite sure of what you want to do. Just hide empty tooltips or get control over when a tip is shown?
Maybe this fits your needs:
private boolean showToolTip = true;
tooltip = new ToolTip(parent.getShell(), SWT.NONE);
public void setShowToolTip (boolean show){
showToolTip = show;
}
MouseTrackListener mouseTrackListener = new MouseTrackListener() {
#Override
public void mouseEnter(MouseEvent e) {
if (text != null && !text.isEmpty()) {
tooltip.setLocation(Display.getCurrent().getCursorLocation().x,
Display.getCurrent().getCursorLocation().y + TOOLTIP_OFFSET_Y);
if (showToolTip){
tooltip.setVisible(true);
} else {
tooltip.setVisible(false);
}
}
Or do you want a solution in which the code in your instance of MouseTrackListerner is not changed?
You could try ToolTip's setAutoHide(boolean autoHide) property. (not used it, so not sure)
if(text == null || text.isEmpty()) {
tooltip.setAutoHide(true);
}
Does this help?
I have made a custom BasicScrollBarUI.
I have replaced the incrButton / decrButton by JLabels.
Therefore i had to override the InstallListeners(), to add the Listener to the incr/decrLabel instead of the JButtons.
//TODO
protected void installListeners() {
trackListener = createTrackListener();
buttonListenerCustom = createArrowButtonListenerCustom();
modelListener = createModelListener();
propertyChangeListener = createPropertyChangeListener();
scrollbar.addMouseListener(trackListener);
scrollbar.addMouseMotionListener(trackListener);
scrollbar.getModel().addChangeListener(modelListener);
scrollbar.addPropertyChangeListener(propertyChangeListener);
// scrollbar.addFocusListener(getHandler());
if (incrLabel != null) {
incrLabel.addMouseListener(buttonListenerCustom);
System.out.println("OK gemacht");
}
if (decrLabel != null) {
decrLabel.addMouseListener(buttonListenerCustom);
}
scrollListener = createScrollListener();
scrollTimer = new Timer(scrollSpeedThrottle, scrollListener);
scrollTimer.setInitialDelay(300); // default InitialDelay?
}
Therefore i had to override the ArrowButtonListener, to react on the Labels.
protected class ArrowButtonListenerCustom extends MouseAdapter {
boolean handledEvent;
public void mousePressed(MouseEvent e) {
if (!scrollbar.isEnabled()) {
return;
}
// not an unmodified left mouse button
// if(e.getModifiers() != InputEvent.BUTTON1_MASK) {return; }
if (!SwingUtilities.isLeftMouseButton(e)) {
return;
}
int direction;
if (e.getSource() == incrLabel) {
direction = 1;
incrLabel.setIcon(new ImageIcon(increaseButtonPressedImage));
} else {
direction = -1;
decrLabel.setIcon(new ImageIcon(decreaseButtonPressedImage));
}
scrollByUnit(direction);
scrollTimer.stop();
scrollListener.setDirection(direction);
scrollListener.setScrollByBlock(false);
scrollTimer.start();
handledEvent = true;
if (!scrollbar.hasFocus() && scrollbar.isRequestFocusEnabled()) {
scrollbar.requestFocus();
}
}
public void mouseReleased(MouseEvent e) {
scrollTimer.stop();
handledEvent = false;
scrollbar.setValueIsAdjusting(false);
incrLabel.setIcon(new ImageIcon(increaseButtonImage));
decrLabel.setIcon(new ImageIcon(decreaseButtonImage));
}
}
Of course createArrowButtonListenerCustom() returns a new instance of ArrowButtonListenerCustom.
Now my Problem:
When I click on the incr/decrLabel, the List scrolls correctly, but the thumb of the ScrollBar doesn't move (or better: the Thumb isn't repainted. If I move the Mouse over the thumb, it gets repainted on the right place). I have the same problem, when I scroll with the MouseWheel.
I don't understand, why this doesn't work.
Thanks for your help!