I would like to make the point info tooltip appear faster. How can i do it? with the default setting I have to hover the mouse onto the point, then wait to be able to see point coordinate information. I want the point coordinates to be immediately available. How can i do that?
ChartPanel provides getInitialDelay() and setInitialDelay() to query and alter "the initial tooltip delay value used inside this chart panel." As a concrete example based on BarChartDemo1, the following change to the constructor eliminates the initial delay entirely:
public BarChartDemo1(String title) {
super(title);
…
chartPanel.setInitialDelay(0);
setContentPane(chartPanel);
}
It's a late solution but here it is.
Here is how i handled with JavaFX.
It shows the tooltip fast instant and tooltip does not fade after a while.
/**
* The "tooltip" is the hard-coded id for the tooltip object.
* It's set inside the JFreeChart Lib.
* */
public static String TOOLTIP_ID = "tooltip";
public static void removeTooltipHandler(ChartViewer chartViewer) {
chartViewer.getCanvas().removeAuxiliaryMouseHandler(chartViewer.getCanvas().getMouseHandler(TOOLTIP_ID));
}
public static void addFasterTooltipHandler(ChartViewer chartViewer) {
if(chartViewer.getCanvas().getMouseHandler(TOOLTIP_ID) != null) {
removeTooltipHandler(chartViewer);
}
chartViewer.getCanvas().addAuxiliaryMouseHandler(new TooltipHandlerFX(TOOLTIP_ID) {
Tooltip tooltip;
boolean isVisible = false;
#Override
public void handleMouseMoved(ChartCanvas canvas, MouseEvent e) {
if (!canvas.isTooltipEnabled()) {
return;
}
String text = getTooltipText(canvas, e.getX(), e.getY());
setTooltip(canvas, text, e.getScreenX(), e.getScreenY());
}
private String getTooltipText(ChartCanvas canvas, double x, double y) {
ChartRenderingInfo info = canvas.getRenderingInfo();
if (info == null) {
return null;
}
EntityCollection entities = info.getEntityCollection();
if (entities == null) {
return null;
}
ChartEntity entity = entities.getEntity(x, y);
if (entity == null) {
return null;
}
return entity.getToolTipText();
}
// This function is copied from Canvas.setTooltip and manipulated as needed.
public void setTooltip(ChartCanvas canvas, String text, double x, double y) {
if (text != null) {
if (this.tooltip == null) {
this.tooltip = new Tooltip(text);
this.tooltip.autoHideProperty().set(false); // Disable auto hide.
Tooltip.install(canvas, this.tooltip);
} else {
this.tooltip.setText(text);
this.tooltip.setAnchorX(x);
this.tooltip.setAnchorY(y);
}
this.tooltip.show(canvas, x, y);
isVisible = true;
} else {
if(isVisible) {
this.tooltip.hide();
isVisible = false;
}
}
}
});
}
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();
}
}
}
}
We have a large Java app that is used on both Windows and OSX.
We do custom Drag and Drop between 2 of our JTables.
On Windows, this works perfectly. The custom cursor is displayed as you drag over the target JTable.
On the Mac, the custom cursor is never displayed. Instead a gray rectangle (border only) is displayed when you start dragging. This rectangle is the width of the table column, and the height of the table. Our logging is showing that the dragOver() and dropActionChanged() methods are getting called, and we are setting the custom cursor. It just never gets displayed.
If I disable our custom cursor code, the same box is displayed - but it has the Circle/slash icon in the middle as well.
I want to get rid of the weird box, and display the custom cursor.
Excerpts from the code:
private class FileTransferHandler extends TransferHandler {
private static final long serialVersionUID = 1L;
private final Logger log = LogManager.getLogger();
private final CursorDragSourceListener dragSourceListener = new CursorDragSourceListener();
// Left out the Drop handling code that was here
#Override
public int getSourceActions( final JComponent c) {
log.debug("FileTransferHandler.getSourceAction: ");
return COPY | MOVE;
}
#Override
protected Transferable createTransferable( final JComponent c) {
log.debug("FileTransferHandler.createTransferable:");
List<iFilePage> pages = new ArrayList<iFilePage>();
// Left out the code that builds the pages list
DragSource.getDefaultDragSource().addDragSourceListener(dragSourceListener);
dragSourceListener.setCursorChoice(pages.size() == 1);
return new FilePageTransferable(pages);
}
#Override
protected void exportDone( final JComponent c,
final Transferable t,
final int action) {
log.debug("FileTransferHandler.exportDone: {}", action, t);
tblFixed.getSelectionModel().clearSelection();
DragSource.getDefaultDragSource().removeDragSourceListener(dragSourceListener);
return;
}
}
private static class CursorDragSourceListener implements DragSourceListener {
private Cursor singlePage = null;
private Cursor multiPage = null;
private Cursor badSinglePage = null;
private Cursor useCursor = null;
private boolean useSingle = false;
public CursorDragSourceListener() {
Toolkit toolkit = Toolkit.getDefaultToolkit();
URL url;
String name;
Image img;
url = FileUtils.getResourceURL("/images/page.png");
name = "DragPage";
img = toolkit.createImage(url);
singlePage = toolkit.createCustomCursor(img, new Point(16, 16), name);
url = FileUtils.getResourceURL("/images/badpage_stack.png");
name = "DragBadPage";
img = toolkit.createImage(url);
badSinglePage = toolkit.createCustomCursor(img, new Point(16, 16), name);
url = FileUtils.getResourceURL("/images/page_stack.png");
name = "DragPageStack";
img = toolkit.createImage(url);
multiPage = toolkit.createCustomCursor(img, new Point(16, 16), name);
return;
}
public void setCursorChoice( final boolean single) {
log.debug("CursorDragSourceListener.setCursorChoice: {}", single);
useSingle = single;
if (useSingle) {
useCursor = singlePage;
} else {
useCursor = multiPage;
}
return;
}
#Override
public void dropActionChanged( final DragSourceDragEvent dsde) {
log.debug("CursorDragSourceListener.dropActionChanged: {}", dsde.getDropAction(), useSingle);
if (dsde.getDropAction() == 2) {
if (!useSingle) {
useCursor = badSinglePage;
} else {
useCursor = singlePage;
}
} else {
if (useSingle) {
useCursor = singlePage;
} else {
useCursor = multiPage;
}
}
dsde.getDragSourceContext().setCursor(useCursor);
return;
}
#Override
public void dragOver( final DragSourceDragEvent dsde) {
try {
Object x = dsde.getDragSourceContext().getTransferable()
.getTransferData(DataFlavor.javaFileListFlavor);
log.trace("CursorDragSourceListener.dragOver: {}", (x != null) ? x.getClass().getSimpleName() : "null");
if (x instanceof ArrayList) {
dsde.getDragSourceContext().setCursor(useCursor);
}
} catch (Exception e) {
log.error("CursorDragSourceListener.dragOver:", e);
}
}
#Override
public void dragExit( final DragSourceEvent dse) {
}
#Override
public void dragEnter( final DragSourceDragEvent dsde) {
}
#Override
public void dragDropEnd( final DragSourceDropEvent dsde) {
}
}
After a bunch more checking and analysis, it turns out that our Custom Selection Model was causing this problem on OSX.
We have a selection model that allows you to select multiple individual cells, not just whole rows.
So the getMinSelectionindex() and getMaxSelectionIndex() methods returned dummy data, since we never used them.
That works fine on MS Win, but apparently the OSX drag and drop for JTable uses those calls.
After modifying our code to return reasonable values, the selection box is no longer as tall as the table.
The custom cursors appear most of the time, but still randomly disappear for no apparent reason.
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;
}
}
}
});
}
}
});
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!
I have a JPopupMenu object. Its behavior depends on its coordinates.
How can I get its position relatively to its parent container?
In your MouseListener's method (mouseReleased etc.) you should receive a MouseEvent object containing the current position. If you don't want to use those values you can try using the Component#getLocation method, otherwise the Component#getLocationOnScreen but it returns the absolute position, then you need to calculate the relative one.
There is a solution, but it is not recommended, because if there is a SecurityManager it may fail (force the field to be accessible):
public static Container getTopParent(#Nonnull Component c) {
Container lastNotNull = (Container) c;
Container p = c.getParent();
if (p != null)
lastNotNull = p;
while(p != null) {
lastNotNull = p;
p = p.getParent();
}
return lastNotNull;
}
public static int getClickedXThatInvokedPopup(#Nonnull ActionEvent ev) {
try {
JPopupMenu topParent = (JPopupMenu) getTopParent((Component) ev.getSource());
java.lang.reflect.Field fieldX = topParent.getClass().getDeclaredField("desiredLocationX");
fieldX.setAccessible(true);
int x = (Integer) fieldX.get(topParent);
Point p = new Point(x, 0);
SwingUtilities.convertPointFromScreen(p, topParent.getInvoker());
return p.x;
} catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
System.err.println("Cannot get clicked point: " + ex);
return -1;
}
}
Since neither the Component#getLocation nor the Component#getLocationOnScreen method worked for me, and the fields desiredLocationX/desiredLocationY are not accessible, I extended JPopupMenu() as follows:
contextMenu = new JPopupMenu(){
private Point desiredLocation;
/**
* Override Component#getLocation, since it always returns 0,0.
*/
#Override
public Point getLocation() {
return desiredLocation;
}
#Override
public void show(Component invoker, int x, int y) {
desiredLocation = new Point(x, y);
super.show(invoker, x, y);
}
};