JavaFX: how to set correct tooltip position? - position

I have a TextField for which I want a Tooltip to be shown under some circumstances.
After performing checks I run the followig code:
textFieldUsername.setTooltip(new Tooltip("Enter username!"));
textFieldUsername.getTooltip().setAutoHide(true);
textFieldUsername.getTooltip().show(textFieldUsername, 1, 1);
So when somebody tries to login with empty username he gets a prompting Tooltip over the "username" TextField.
But when it comes to action, the Tooltip pops up in the top left corner of the screen.
Should I calculate coords of my scene, then add my TextField coords to them, or there is a way to set these 1, 1 arguments from the call of show() to be relative to the TextField position?

I think the coordinates are always relative to the screen. To calculate component coordinates you need to incorporate scene and window coordinates.
Point2D p = label.localToScene(0.0, 0.0);
label.getTooltip().show(label,
p.getX() + label.getScene().getX() + label.getScene().getWindow().getX(),
p.getY() + label.getScene().getY() + label.getScene().getWindow().getY());

Related

Can't get QGraphicsEllipseItem() to display

I can't seem to get a QGraphicsEllipseItem to show up in my view. I am attempting to use it in combination with a QGraphicsLineItem:
# Instantiate the line object:
self.profileLine = QGraphicsLineItem()
self.profileLine.setPen(QPen(Qt.yellow, 1.0))
self.profileLine.setLine(self.StartX, self.StartY, self.StopX, self.StopY)
self.scene.addItem(self.profileLine)
# Instantiate the circle:
self.profileStopHandle = QGraphicsEllipseItem()
self.profileStopHandle.setPen(QPen(Qt.yellow, 1.0))
self.profileStopHandle.setRect(self.StopX, self.StopY, 50, 50)
self.scene.addItem(self.profileStopHandle)
Later, in my mouse move event, I determine the current mouse coordinates, assign them to StopX/StopY and redraw the line from the start point to the new stop point as well as draw the ellipse around the new stop point:
self.profileLine.setLine(self.StartX, self.StartY, self.StopX, self.StopY)
self.profileStopHandle.setPos(self.StopX, self.StopY)
The line shows up fine and behaves just as it should, but no matter what I cannot seem to get the ellipse to draw. I know the point coordinates I'm passing to it are correct because they are the same ones I'm using for the line. The ellipse just never appears as if it was never created in the first place. What am I doing wrong here (it must be something very basic)? Thank you in advance.
The issue was the Z-value (amateur mistake). I brought the ellipse to the foreground and now it is visible:
self.profileStopHandle.setZValue(self.sceneImage.zValue() + 1.0)

AndroidPlot - Labels and text

I am a non-developer product manager for an application built in both Android and iOS. We have a bar graph in iOS that provides text for the content of the graph. It displays Totals for each bar, and percentages for each segment of each bar.
In Android, using AndroidPlot (so I understand) we just display the bars with different color segments and no percent totals or totals. I am told by the developer that we can't show more.
I would display the images here, but stackoverflow tells me I don't have enough reputation points to do this. I have created a link to my dropbox with the images https://www.dropbox.com/sh/2uocm5bn79rerbe/AAB7s9QEEYIRIgXhKbUAaOyDa
Is it possible to use AndroidPlot to emulate this iOS chart or at least represent to same information to the end user?
Your developer is more or less correct but you have options. Androidplot's BarRenderer by default provides only an optional label at the top of each bar, which in your iOS images is occupied by the "available", "new", "used" and "rent" label. That label appears to be unused in your Android screenshot so one option would be to utilize those labels do display your totals.
As far as exactly matching the iOS implementation with Androidplot, the missing piece is the ability to add additional labels horizontally and vertically along the side of each bar. You can extend BarRenderer to do this by overriding it's onRender(...) method. Here's a link for your developer that shows where in the code he'll want to modify onRender(...).
I'd suggest these modifications to add the vertical labels:
Invoke Canvas.save(Canvas.ALL_SAVE_FLAG) to store the default orientation of the Canvas.
Use Canvas.translate(leftX, bottom) to center on the bottom left point of the bar
Rotate the Canvas 90 degrees using Canvas.rotate(90) to enable vertical text drawing
Draw whatever text is needed along the side of the plot; 0,0 now corresponds to the bottom left corner of the bar so start there when invoking canvas.drawText(x,y).
Invoke Canvas.restore() to restore the canvas' original orientation.
After implementing the above, adding horizontal "%" labels should be self evident but if you run into trouble feel free to ask more questions along the way.
UPDATE:
Here's a very basic implementation of the above. First the drawVerticalText method:
/**
*
* #param canvas
* #param paint paint used to draw the text
* #param text the text to be drawn
* #param x x-coord of where the text should be drawn
* #param y y-coord of where the text should be drawn
*/
protected void drawVerticalText(Canvas canvas, Paint paint, String text, float x, float y) {
// record the state of the canvas before the draw:
canvas.save(Canvas.ALL_SAVE_FLAG);
// center the canvas on our drawing coords:
canvas.translate(x, y);
// rotate into the desired "vertical" orientation:
canvas.rotate(-90);
// draw the text; note that we are drawing at 0, 0 and *not* x, y.
canvas.drawText(text, 0, 0, paint);
// restore the canvas state:
canvas.restore();
}
All that's left is to invoke this method where necessary. In your case it should be done once per BarGroup and should maintain a consistent position on the y axis. I added the following code to the STACKED case in BarRenderer.onRender(...), immediately above the break:
// needed some paint to draw with so I'll just create it here for now:
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(PixelUtils.spToPix(20));
drawVerticalText(
canvas,
paint,
"test",
barGroup.leftX,
basePositionY - PixelUtils.dpToPix(50)); // offset so the text doesnt intersect with the origin
Here's a screenshot of the result...sorry it's so huge:
Personally, I don't care for the fixed y-position of these vertical labels and would prefer them to float along the upper part of the bars. To accomplish this I modify my invocation of drawVerticalText(...) to look like this:
// needed some paint to draw with so I'll just create it here for now:
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(PixelUtils.spToPix(20));
// right-justify the text so it doesnt extend beyond the top of the bar
paint.setTextAlign(Paint.Align.RIGHT);
drawVerticalText(
canvas,
paint,
"test",
barGroup.leftX,
bottom);
Which produces this result:

D3 - Positioning tooltip on SVG element not working

I have a webpage with an SVG. On some of its shapes I need to display a tooltip. However, I can't get the tooltip to appear where it should, just some pixels away from the shape itself.
It appears way on the right hand side of the screen, maybe some 300px away.
The code I am using to get the coordinates is as follows:
d3.select("body")
.select("svg")
.select("g")
.selectAll("circle")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){
var svgPos = $('svg').offset(),
/*** Tooltip ***/
//This should be the correct one, but is displaying at all working at all.
/*x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;*/
//This displays a tool tip but way much to the left of the screen.
x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;
Tooltip
window.alert("svgPos: "+svgPos+" top: "+y+"px left: "+x+"px "+d3.event.target.cx.animVal.value);
return tooltip.style("top", x+"px").style("left",y+"px");
})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
I got to this code following this SO post.
I have changed $(ev.target).attr(cx) as it is not returning a value on my machine; d3.event.target.cx is, even though it seems it is not affecting the end result anyway.
What am I doing wrong? Could somebody help me please? Thank you very much in advance for your time.
If your tooltip is an HTML element, then you want to position it relative to the page as a whole, not the internal SVG coordinates, so accessing the cx/cy value is just complicating things. I can't say for sure without looking at your code, but if you have any transforms on your <svg> or <g> elements, then that could be what's throwing you off.
However, there is a much easier solution. Just access the mouse event's default .pageX and .pageY properties, which give the position of the mouse relative to the HTML body, and use these coordinates to position your tooltip div.
Example here: http://fiddle.jshell.net/tPv46/1/
Key code:
.on("mousemove", function () {
//console.log(d3.event);
return tooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Even with rotational transforms on the SVG circles, the mouse knows where it is on the page and the tooltip is positioned accordingly.
There are other ways to do this, including getting a tooltip to show up in a fixed location relative to the circle instead of following the mouse around, but I just checked the examples I was working on and realized they aren't cross-browser compatible, so I'll have to standardize them and get back to you. In the meantime, I hope this gets you back on track with your project.
Edit 1
For comparison, here is the same example implemented with both an HTML tooltip (a <div> element) and an SVG tooltip (a <g> element).
http://fiddle.jshell.net/tPv46/4/
The default mouse event coordinates may be great for positioning HTML elements that are direct children of <body>, but they are less useful for positioning SVG elements. The d3.mouse() function calculates the mouse coordinates of the current event relative to a specified SVG element's coordinate system, after all transformations have been applied. It can therefore be used to get the mouse coordinates in the form we need to position an SVG tooltip.
Key code:
.on("mousemove", function () {
var mouseCoords = d3.mouse(
SVGtooltip[0][0].parentNode);
//the d3.mouse() function calculates the mouse
//position relative to an SVG Element, in that
//element's coordinate system
//(after transform or viewBox attributes).
//Because we're using the coordinates to position
//the SVG tooltip, we want the coordinates to be
//with respect to that element's parent.
//SVGtooltip[0][0] accesses the (first and only)
//selected element from the saved d3 selection object.
SVGtooltip
.attr("transform", "translate(" + (mouseCoords[0]-30)
+ "," + (mouseCoords[1]-30) + ")");
HTMLtooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Note that it works even though I've scaled the SVG with a viewBox attribute and put the tooltip inside a <g> with a transform attribute.
Tested and works in Chrome, Firefox, and Opera (reasonably recent versions) -- although the text in the SVG tooltip might extend past its rectangle depending on your font settings. One reason to use an HTML tooltip! Another reason is that it doesn't get cut off by the edge of the SVG.
Leave a comment if you have any bugs in Safari or IE9/10/11. (IE8 and under are out of luck, since they don't do SVG).
Edit 2
So what about your original idea, to position the tooltip on the circle itself? There are definite benefits to being able to position the tip exactly: better layout control, and the text doesn't wiggle around with the mouse. And most importantly, you can just position it once, on the mouseover event, instead of reacting to every mousemove event.
But to do this, you can no longer just use the mouse position to figure out where to put the tooltip -- you need to figure out the position of the element, which means you have to deal with transformations. The SVG spec introduces a set of interfaces for locating SVG elements relative to other parts of the DOM.
For converting between two SVG transformation systems you use SVGElement.getTransformToElement(SVGElement); for converting between an SVG coordinate system and the screen, you use SVGElement.getScreenCTM(). The result are transformation matrices from which you can
extract the net horizontal and vertical translation.
The key code for the SVG tooltip is
var tooltipParent = SVGtooltip[0][0].parentNode;
var matrix =
this.getTransformToElement(tooltipParent)
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
SVGtooltip
.attr("transform", "translate(" + (matrix.e)
+ "," + (matrix.f - 30) + ")");
The key code for the HTML tooltip is
var matrix = this.getScreenCTM()
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
absoluteHTMLtooltip
.style("left",
(window.pageXOffset + matrix.e) + "px")
.style("top",
(window.pageYOffset + matrix.f + 30) + "px");
Live example: http://fiddle.jshell.net/tPv46/89/
Again, I'd appreciate a confirmation comment from anyone who can test this in Safari or IE -- or any mobile browser. I'm pretty sure I've used standard API for everything, but just because the API is standard doesn't mean it's universally implemented!

Display UIMenuController in UIBarButton Click

I am trying to display a UIMenuController in a Toolbar button. I have the code below but I am unsure as to what should be in the "SetTargetRect" method.
What should I put in the SetTargetRect method to display the Menu?
Is there anything else that is missing from this code?
ToolbarItems = new UIBarButtonItem[] {
new UIBarButtonItem ("Sort", UIBarButtonItemStyle.Bordered, (sender, e) => {
var menu = UIMenuController.SharedMenuController;
menu.MenuItems = new UIMenuItem[] {
new UIMenuItem ("Current", new Selector ("SortRaceEntrants")),
new UIMenuItem ("Movers", new Selector ("SortRaceEntrants")),
new UIMenuItem ("Opening", new Selector ("SortRaceEntrants")),
new UIMenuItem ("Number", new Selector ("SortRaceEntrants"))
};
//menu.SetTargetRect ();
menu.SetMenuVisible (true, true);
}),
};
UIBarButtonItem inherits from UIBarItem and NSObject, so finding a frame for them is not easy if not impossible. UINavigationBar inherits from UIView and thus is a view (go figure) and has a frame, etc.
What I do is point to a rect inside that view where the UIBarButtonItem should be.
For example:
[myMenu setTargetRect:CGRectMake(10, 10, 20, 25)
inView:self.navigationController.navigationBar];
This will make the UIMenuController 'point' at the leftBarButtonItem.
The same technique can be used for the toolbar.
In your case something like:
[menu setTargetRect:CGRectMake(0,0,40,40)
inView:self.navigationController.toolBar];
would point to the center of the toolBar and thus the center button in the toolBar
According to the API docs, the target rect defines the area that you want to display the menu relative to - iOS will either show the menu above or below the area defined by TargetRect.
When you make this menu visible, UIMenuController positions it
relative to a target rectangle on the screen; this rectangle usually
defines a selection. The menu appears above the target rectangle or,
if there is not enough space for it, below it. The menu’s pointer is
placed at the center of the top or bottom of the target rectangle, as
appropriate. Be sure to set the tracking rectangle before you make the
menu visible. You are also responsible for detecting, tracking, and
displaying selections.
and
This target rectangle (targetRect) is usually the bounding rectangle
of a selection. UIMenuController positions the editing menu above this
rectangle; if there is not enough space for the menu there, it
positions it below the rectangle. The menu’s pointer is placed at the
center of the top or bottom of the target rectangle as appropriate.
Note that if you make the width or height of the target rectangle
zero, UIMenuController treats the target area as a line or point for
positioning (for example, an insertion caret or a single point).
Once it is set, the target rectangle does not track the view; if the
view moves (such as would happen in a scroll view), you must update
the target rectangle accordingly.

How to add a vertial scrollbar in Canvas?

I am developing a Java ME application using Canvas. The details displayed on the Canvas are plentiful, so I need to scroll down the screen to view the further details.
But how can I add a vertical scrollbar on a Canvas?
I think that the scrollbar should be drawn inside the paint method. And you implement the keyReleased method when clicking the arrow of the scrollbar. To implement the scrolling I would prefer call repaint(); and I will manage the paint method with boolean or some other type of private variable.
Consider your canvas as the source and the scroll bar as destination use the following formula to find the scroll bar position
Target point X co-ordinate: tX = taX + ((tW * (sX - saX)) / sW)
Target point Y co-ordinate: tY = taY - ((tH * (saY - sY)) / sH)
saX : Source axis start X coordinate
saY : Source axis start Y coordinate
sW : Souce Width
sH : Source Height
taX : Target axis start X coordinate
taY : Target axis start Y coordinate
tW : Target Width
tH : Target Height
The target height would be the height of the canvas. You must draw the scroll bar as the topmost element.

Resources