Adding label to PolylineConnection in Draw2D - eclipse

I'm trying to add a label to a PolylineConnection in Draw2d. I'm using the example in java2s as a basis. The problem is that even if I can create the text by using graphics.drawText() on the paintFigure method from the PathFigure object (that extends PolylineConnection), the label is cut out most of the time, as shown in these captures:
To me, it looks like the bounds of the figure are leaving part of the text outside from the paint area, as it does indeed paint correctly in diagonal arrows, which have bigger bounds.
I have tried to set explicitly the bounds of the object, both in constructor and paint methods, but it seems like the PolylineConnection is ignoring them. Any idea of how to solve this or if there is another way of achieving this kind of label?

Please use below figure for your connection figure.
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.MidpointLocator;
import org.eclipse.draw2d.PolygonDecoration;
import org.eclipse.draw2d.PolylineConnection;
public class LabelConnectionFigure extends PolylineConnection {
protected Label label;
public LabelConnectionFigure() {
setTargetDecoration(new PolygonDecoration());
MidpointLocator labelLocator = new MidpointLocator(this, 0);
label = new Label("1");
label.setOpaque(true);
add(label, labelLocator);
}
public void setLabelText(String labelText) {
label.setText(labelText);
}
public String getLabelText() {
return label.getText();
}
}

Related

Text element with graphical text effects

I want create Text elements (com.itextpdf.layout.element.Text) with different kind of additional graphical effects, for example a drop shadow or adding some kind of texture on the glyphs. (see DropShadow some 3D Effect)
What is the best way of achiving this?
The best idea i had so far, is using the clipping Text Rendering mode. (Defined in PDF 32000-1 9.3.6; com.itextpdf.kernel.pdf.canvas.PdfCanvasConstants.TextRenderingMode). Drawing the Text as clipping boundary and the apply some kind of Texture or drawing an additional shadow "layer". However the clipping path is restored to the state before text drawing with canvas.restoreState(), which is called at the end in com.itextpdf.layout.renderer.TextRender#draw. Extending this to a custom TextRenderer could work, but the draw function is a big one with some calls to private functions of TextRenderer.
Any adivces for other possible methods?
I think in general customization of that level will require quite come code anyway. Completely overriding draw may indeed not work because some private implementation details are not exposed to the public. One option is of course to duplicate those implementation details into your custom renderer.
Another idea is to plug into the PdfCanvas which does low-level drawing. You can create your own wrapper like the following one and delegate all operations to the PdfCanvas instance you wrap around except a couple of "interesting" operations where you will customize the logic and apply some styling:
private static class PdfCanvasWrapper extends PdfCanvas {
private PdfCanvas delegate;
public PdfCanvasWrapper(PdfCanvas wrapped) {
super(wrapped.getContentStream(), wrapped.getResources(), wrapped.getDocument());
this.delegate = wrapped;
}
// "Interesting" methods
#Override
public PdfCanvas endText() {
delegate.endText();
delegate.setFillColor(ColorConstants.BLACK);
delegate.rectangle(10, 10, 300, 300);
delegate.fill();
return this;
}
// "Boring" methods - just delegate the implementation to the wrapped instance
#Override
public PdfCanvas beginVariableText() {
delegate.beginVariableText();
return this;
}
#Override
public PdfCanvas endVariableText() {
delegate.endVariableText();
return this;
}
// Override all other members like above
}
In this case your custom text renderer will only plug in the right DrawContext but use the default draw operation:
private static class CustomTextRenderer extends TextRenderer {
public CustomTextRenderer(Text textElement) {
super(textElement);
}
#Override
public void draw(DrawContext drawContext) {
DrawContext newContext = new DrawContext(drawContext.getDocument(), new PdfCanvasWrapper(drawContext.getCanvas()));
super.draw(newContext);
}
#Override
public CustomTextRenderer getNextRenderer() {
return new CustomTextRenderer((Text) modelElement);
}
}
Main could could look like this:
Paragraph p = new Paragraph();
Text text = new Text("Hello");
text.setTextRenderingMode(TextRenderingMode.CLIP);
text.setNextRenderer(new CustomTextRenderer(text));
p.add(text);
In general this approach is also hacky and of course depends on the implementation details as much as the initial approach you suggested. The approach you suggested is a more stable one but requires more code and probably more tuning when you update to the new version of the library. The approach I described above is more hacky but it results in less business logic copy-pasting and maybe easier to maintain.

How can I set a public variable using getChildIndex in as3?

I was coding a very simple program that lets you move around a circle, with also a rectangle in the stage. I wanted to make the circle get in front of the rectangle while you are dragging it, but when you released the mouse, the circle would be sent back.
I don't know how to set a public variable using the getChildIndex method. I don't really care about the rest of the code. I'm mainly interested in how can I make the getChildIndex method work with a public variable.
package code
{
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.Sprite;
public class Main extends MovieClip
{
public var myCircleIndex:int = getChildIndex(myCircle);
public function Main()
{
myCircle.addEventListener(MouseEvent.MOUSE_DOWN, mouseClicking);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
}
public function mouseClicking(e:MouseEvent): void
{
myCircle.startDrag();
setChildIndex(myCircle, numChildren-1);
}
public function mouseReleased(e:MouseEvent): void
{
myCircle.stopDrag();
setChildIndex(myCircle, myCircleIndex);
}
}
}
I'm using an instance ("myCircle") that I created directly in the stage as a movie clip.
The problem is in the public var I set at the beginning, it doesn't let me get the child index of myCircle, but if I put the same line inside a function, it works.
I know I could directly put the index number of myCircle in the last line (and erasing the public var myCircleIndex), but I figured out that there would be a way of using the getChildIndex for a public var in a class.
How do you use getChildIndex in a public variable inside a class?
The reason it doesn't work, is because your timeline objects don't yet exist when the line public var myCircleIndex:int runs.
You shouldn't try and access non-primitive objects in your class level variable declarations for this very reason, as nothing else in the class is available yet when those vars are created.
Here is how you can refactor this (see the code comments):
public class Main extends MovieClip
{
public var myCircleIndex:int; //just create the reference here, don't assign it
public var myCircle:flash.display.DisplayObject; //this line is just for better compile time checking and code completion, completely optional
public function Main()
{
//wait for all the display stuff to be created before trying to access it. The constructor function can run before timeline stuff is created, so it's not safe to reference stage or timeline objects here.
if(!stage){
this.addEventListener(Event.ADDED_TO_STAGE, timelineCreated);
}else {
timelineCreated(null);
}
}
private function timelineCreated(e:Event):void {
//now that we're certain the timeline stuff has been created, we can reference timeline objects and stage:
//store the initial z-index of myCircle
myCircleIndex = getChildIndex(myCircle);
//the rest of your code that was in the construction -Main()- before
myCircle.addEventListener(MouseEvent.MOUSE_DOWN, mouseClicking);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseReleased);
}
//no change to any of the following stuff
public function mouseClicking(e:MouseEvent): void
{
myCircle.startDrag();
setChildIndex(myCircle, numChildren-1);
}
public function mouseReleased(e:MouseEvent): void
{
myCircle.stopDrag();
setChildIndex(myCircle, myCircleIndex);
}
}
All you need to do to put the circle behind the square is on release do addChild(myRectangle) or addChildAt(myCircle, 0);
You are overcomplicating things by trying to track a variable in my opinion. Let flash sort it out behind the scenes.
If you want a little more finesse and want to just put the circle directly behind the square (if there were 100 layers and the square is at level 12, but you aren't sure which level the square is at) you could do
addChildAt(myCircle, getChildIndex(myRectangle)-1);
note
setChildIndex(myCircle, numChildren-1);
That's fine to do it that way. The more common way to do this is just
addChild(myCircle);
It does the exact same thing. Many people are confused by this thinking this would add a new myCircle but it just brings it to the front if it's already in the display list, and if it's not in the display list, it adds it to the display list at the top z-order (numChildren-1).

Setting a textfield in a class in but displaying a number?

I'm learn as3, and building some exercises from the adobe online tutorials. I'm trying to do a very simple task and I think my code is exactly the same as the tutoriala nd it's not working! I just can't work it out...
I'm trying to change the textfield in a button from that buttons class.
The button is called GameButton, the textfield is called blabel and is classic text and dynamic text. Here is the code for the GameButton class. Instead of displaying "Click" as below it just changes the label to the number 1. The trace statement is working etc it is going there, but the text isn't passing through or something. Please help!!!
package {
import flash.display.MovieClip;
public class GameButton extends MovieClip {
public function GameButton() {
trace("Gamebutton has been created");
this.blabel.text = "Click";
stop();
}
}
}
The long and short of it is you can create the button in code, or else you can try listening for added to stage events coming from the parent object you're adding the children to (maybe the stage, maybe another DisplayObjectContainer). The problem with the listening method is I'm not sure how you would know which child just dispatched the event without making some messy code. I think the first option is generally easier and makes more sense, the only caveat is that you have to place the instances using x/y coordinates or apply scaleX, scaleY to stretch or shrink objects instead of doing it using the authoring tool. You can still use the drag and drop parts of flash to figure out coordinates and build individual movie clips etc.
Enough talk on to some code:
package
{
import flash.display.MovieClip;
public class GameButton extends MovieClip {
private var blabel:TextField; //This can be any display object class or a class that extends from a display object class (Sprite, MovieClip, MyCustomButton etc.)
public function GameButton() {
blabel = new TextField(); //The type after new, should be the same, or a sub-class (extension) of the type used in the variable declaration above
addChild(blabel);
//blabel.x=10; //optional over ten pixels from left
//blabel.y=10; //optional down ten pixels from top
//blabel.scaleX=.5; //optional half the width
//blabel.scaleY=2; //optional 2 times taller
trace("Gamebutton has been created");
blabel.text = "Click";
stop();
}
}
}

How to create a GEF figure with separate label?

I've been trying to create a Draw2D Figure that consists of two parts - a central resizeable shape, such as a circle or rectangle, and an editable label for the bottom part. An example of this type of figure is the icon/label you see on a computer's Desktop.
The first attempt was to create a parent container figure with two child sub-figures - a shape figure placed centrally and a label placed at the bottom. It also implemented HandleBounds so that selection and resizing occurs only on the upper shape sub-figure. This turned out not to be a working solution because as the label gets wider with more text so does the main parent figure and consequently the central shape figure. In other words the overall parent figure is as wide as the child label figure.
What I'm seeking is a Figure that maintains the size of the shape figure but allows the width of the label figure to grow independently. Exactly the same behaviour as a desktop icon.
Ok I get your question right now. It's impossible to do what you want:
The parent figure can't be smaller than one of its children or this child will not be visible !!!
You have to create a container figure as you mentioned with an XYLayout and "manually" place and "size" the 2 (the shape and the label) children figure inside this layout using the IFigure.add(IFigure child, Object constraint) method with a Constraint of type Rectangle (Draw2d)
Edit with code sample
Here is an example of what your figure class could look like:
package draw2dtest.views;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Ellipse;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureListener;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
public class LabeledFigure extends Figure {
private final Figure shapeFigure;
private final Label labelFigure;
private Rectangle customShapeConstraint;
public LabeledFigure(String label) {
setLayoutManager(new XYLayout());
setBackgroundColor(ColorConstants.lightGray);
setOpaque(true);
shapeFigure = new Ellipse();
this.add(shapeFigure);
shapeFigure.setBackgroundColor(ColorConstants.yellow);
shapeFigure.addMouseListener(new MouseListener.Stub() {
#Override
public void mousePressed(MouseEvent me) {
customShapeConstraint = new Rectangle(
(Rectangle) LabeledFigure.this.getLayoutManager()
.getConstraint(shapeFigure));
customShapeConstraint.width -= 6;
customShapeConstraint.x += 3;
LabeledFigure.this.getLayoutManager().setConstraint(
shapeFigure, customShapeConstraint);
LabeledFigure.this.revalidate();
}
});
labelFigure = new Label(label);
labelFigure.setOpaque(true);
labelFigure.setBackgroundColor(ColorConstants.green);
labelFigure.addMouseListener(new MouseListener.Stub() {
#Override
public void mousePressed(MouseEvent me) {
Rectangle shapeFigureConstraint = new Rectangle(0, 0,
bounds.width, bounds.height - 15);
LabeledFigure.this.getLayoutManager().setConstraint(
shapeFigure, shapeFigureConstraint);
LabeledFigure.this.revalidate();
}
});
this.add(labelFigure);
this.addFigureListener(new FigureListener() {
#Override
public void figureMoved(IFigure source) {
Rectangle bounds = LabeledFigure.this.getBounds();
Rectangle shapeFigureConstraint = new Rectangle(0, 0,
bounds.width, bounds.height - 15);
LabeledFigure.this.getLayoutManager().setConstraint(
shapeFigure, shapeFigureConstraint);
Rectangle labelFigureConstraint = new Rectangle(0,
bounds.height - 15, bounds.width, 15);
if (customShapeConstraint != null) {
labelFigureConstraint = customShapeConstraint;
}
LabeledFigure.this.getLayoutManager().setConstraint(
labelFigure, labelFigureConstraint);
}
});
}
}
This is not a clean class but it should be a good entry to show you how to achieve your goal. This is an example based on pure Draw2d without any Gef code, thus the resizing of the shape is done by clicking in the yellow Ellipse (the size is decreased) and on the green label (the initial size is restored)
To test this class I created a simple Eclipse view as following:
#Override
public void createPartControl(Composite parent) {
FigureCanvas fc = new FigureCanvas(parent, SWT.DOUBLE_BUFFERED);
fc.setBackground(ColorConstants.red);
Panel panel = new Panel();
panel.setLayoutManager(new XYLayout());
LabeledFigure labeledFigure = new LabeledFigure("This is the label");
fc.setContents(panel);
panel.add(labeledFigure, new Rectangle(10,10, 200,100));
}
Hope this can help,
Manu

How do I set first column width on GWT CellBrowser

There is a bug preventing the setting of the first column width of the CellBrowser widget. There is also a workaround, explained here
http://groups.google.com/group/google-web-toolkit/browse_thread/thread/4fc39b5805833ea2
Apparently it works, but can anybody explain how to subclass the CellBrowser to make it work? Please show me some code.
CellBrowser cellBrowser = new CellBrowser(model, null) {
// HACK: workaround for setDefaultColumnWidth not setting the width of the first column!
// SEE: https://groups.google.com/forum/?pli=1#!topic/google-web-toolkit/T8Ob...
public void setDefaultColumnWidth(int width) {
super.setDefaultColumnWidth(width);
SplitLayoutPanel splitPanel = (SplitLayoutPanel) getWidget();
splitPanel.setWidgetSize(splitPanel.getWidget(0), width);
}
};
cellBrowser.setDefaultColumnWidth(300);
from the thread linked to in the question: http://groups.google.com/group/google-web-toolkit/browse_thread/thread/4fc39b5805833ea2
If you wanted a re-usable class with this fix in (which would probably be a good idea), it's simple enough to convert this anonymous subclass into a regular subclass:
public class FixedCellBrowser<T> extends CellBrowser<T> {
public FixedCellBrowser(TreeViewModel model, T root) {
super(model, root);
}
public void setDefaultColumnWidth(int width) {
super.setDefaultColumnWidth(width);
SplitLayoutPanel splitPanel = (SplitLayoutPanel) getWidget();
splitPanel.setWidgetSize(splitPanel.getWidget(0), width);
}
}
(Note: I have not tried compiling this code.)