I have an application with some labels and containers. I want to drag the labels into the containers and assert that the containers receive a specific label. (A game for kids [starting with my 6-year-old daughter, who is learning to read], where the next level can be accessed after the labels are arranged in a specific order to form words or phrases.)
For the moment, I am using something like this :
label_1.addDropListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
String string = label_1.getParent().toString();
}
But this approach only gets the value of the container where the label has been before, and not the value of the drop target.
I could get the current position by a new button, but I'd rather solve this through the drag & drop operation.
There seems to a draggingOver method (like in JavaFX), but I can't figure out a way to use it.
Any kind hint would be appreciated.
The best approach is probably to subclass Container where you can override the drag over. In that case you will also know the container instance.
In the case of the listener you would need one listener per container and that way you could have the container reference.
Related
When you clear Stage 1, a button is created. Press the button to move to stage 2.
When you clear stage 2, a button is created. Press the button to move to stage 3.
3, then 4, then 5..
Can I make this into one script when moving on to the next scene?
SceneManager.LoadScene("Stage2"); // <-- This method requires multiple scripts.
There are probably many ways .. but as AlexeyLarionov said:
Why not have a
public string targetSceneName;
// or also
//[SerializeField] private string targetSceneName;
configure it in the Inspector and then use
SceneManager.LoadScene(targetSceneName);
You might also be interested in having a Scene field in the Inspector to make this way easier ;)
Alternatively if these anyway are sequencial levels you can also simply go by build index and do e.g.
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
A nice solution to this would be to have a StageManager class, which holds a List<string> levels of your levels by scene name and an int currentLevel for the current level. Then your StageManager class can have a convenience static method
public void LoadNextLevel()
{
currentLevel++;;
string nextLevel = levels[currentLevel];
SceneManager.LoadScene(nextLevel);
}
The convenience of this is that it would be easier to control the scene flow and less dependent on the order of the scenes in your build settings. If you make the levels properly public or add [SerializeField], you can then easily add new entries to the list and even rearrange them quite simply from the editor. Creating the class in such a way that the method is static would also be very convenient.
I've a custom image cell in a CellTree. I want to render two different images depending on the row/node selection state, for example if the row/node is selected I want to render image A if not selected image B. The images couple is different for each node.
What is the best way to get the selection state in the render method of the cell?
CSS solution
If you can work with background images here, the easiest and most efficient solution would be CSS based.
Take a look at /com/google/gwt/user/cellview/client/CellTree.css (in gwt-user.jar). There you see the css classes ".cellTreeItem" and ".cellTreeSelectedItem". The latter one already has an image. You could assign it your own, and for ".cellTreeItem" a different one.
For general information how to adjust CellTable/CellTree/... styles, see e.g. https://stackoverflow.com/a/6387210/291741
Cell solution
You could construct your cell with the SelectionModel like
public MyCell(SelectionModel selectionModel) {
this.selectionModel = selectionModel;
}
public void render(final Cell.Context context, final Node value,
final SafeHtmlBuilder sb) {
if (selectionModel.isSelected(...
}
However, you will probably need to retrigger the cell rendering when the selection changes. It's probably possible, but I've never done that.
I am using the wonderful GWTQuery library to add drag drop support to my GWT cell widgets.
I have a CellTable, and a CellTree both in different modules of my application (I am using GWTP, so everything is decoupled). Neither of these widgets are allowed to know about each other, they simply accept draggables/droppables, check the underlying datatype and then handle them appropriately.
The problem I am having is that I need to support dragging "in between" my cell tree nodes. IE: The typical functionality where if you drag directly over an element in the tree, it drops into that element, BUT if you drag just slightly below or above, you are given a visual indicator (usually a horizontal line) that indicates to the user they can drag the current item in between nodes as well.
And here-in lies the problem, thus far I have no found a way to provide this functionality becuase the setOnDrag() method does not tell me anything about detected droppables, and setOnOver only fires once when it first encounters a droppable.
So far as I can tell this leaves me with only two options:
1.) Add extra "invisible" nodes into my CellTree which are also droppable and sit in between my other nodes.
2.) Implement some custom event handler which I attach to the helper draggable before drag start and use to compare positions of the helper and the droppable once the draggable is actually over the droppable.
Option 1 is really unsavory because it seriously mucks up my CellTree design, and potentially impacts efficiency pretty badly.
Option 2 is really unsavory because it requires a lot of extra code and hacks to get it to work just right.
So I am hoping there is an Option 3 which I might not have though of, any help would be much appreciated.
Cheers,
Casey
I think I have found a solution although it may not be the best, but it is working for me at the moment. In the setOnDrag method, I determine where the item is being dragged at which point I can either add a line before or after the element, or put some css on the element to denote that I am dropping the dragged item on top. I create a GQuery place holder to show the before/after line, and putting a border around element with css for dropping on top.
To know which element I am dropping on top of, I set a global variable in the setOnOver method. Here is a simple mock up:
private GQuery placeHolder = $("<div id='cellPlaceHolder' style=' outline: thin dashed #B5D5FF; height: 2px; background:#B5D5FF;'></div> ");
private Element oldEl = null;
options.setOnOver(new DroppableFunction() {
#Override
public void f(DragAndDropContext context) {
oldEl = context.getDroppable();
}
});
options.setOnDrag(new DragFunction() {
#Override
public void f(DragContext context) {
if (oldEl != null) {
int difference = Math.abs(oldEl.getAbsoluteTop() - context.getHelperPosition().top);
if (difference > 0 && difference < 16) {
/* dragging on top edge, so insert place holder */
oldEl.getFirstChildElement().getStyle().clearProperty("border");
placeHolder.insertBefore(oldEl.getFirstChildElement());
} else if (difference > 26 && difference < 53) {
/* dragging on bottom edge, so insert place holder */
oldEl.getFirstChildElement().getStyle().clearProperty("border");
placeHolder.insertAfter(oldEl.getFirstChildElement());
}else if (difference > 15 && difference < 27) {
/* dragging in middle so add border */
placeHolder.remove();
oldEl.getFirstChildElement().getStyle().setProperty("border", "2px solid red");
}
}
}
});
This way uses several global variables, but it seems to be the best method I have found since the drag options do not include info about the droppable element. And you will have to add the logic to know if it is being dropped before/after/or on and do what you want with it at that point.
I have a flow panel with many photo-widgets inside (gallery with random number of rows and columns, depends on screen size) for which I want to implement drag and drop behavior to change their order. I am using gwt-dnd library. Its FlowPanelDropController allows you to define your own positioner (delimiter) which shows the candidate location for dropping the dragged widget.
I want this positioner to be the empty space with defined width, and the challenging thing is to implement sliding animation effect for the when positioner is added and removed.
If you are a desktop Picasa app user you know what I mean: the target row slides both sides (little to the left, little to the right) extending the space between the items where you are going to drop a photo.
The whole thing is complex enough, but any help related to how to apply the animation for positioner attach/detach is appreciated. Maybe I need to use a different approach (e.g., use GWT native dnd instead of gwt-dnd lib and no "positioners" at all) if you have any ideas how this could be helpful.
Thanks.
Well, I ended up overriding AbstractPositioningDropController (parent of FlowPanelDropController) and adding some extra features.
1) newPositioner() method now builds the Label, which is vertical space with some small width, height and decoration. This widget's element has constant id (say, "POSITIONER"), which helps to distinguish between multiple positioners if you plan to have several of them while navigating with a drag object over multiple drop targets. Also some transition CSS effects were applied to the Label, which will be responsible for handling animated extension of Label's width.
2) in onEnter() I do the following
...
removePositioner(getPositionerElement());
Widget positioner = newPositioner();
dropTarget.insert(positioner, targetIndex);
animatePositionerExtension();
where getPositionerElement() returns DOM.getElementById(POSITIONER)
At the same time removePositioner(..) resets the id of this element to something abstract and ideally should provide some animation before calling .removeFromParent(). But I didn't have enough time to properly debug this so ended up just removing the old positioner with no animation.
Method animatePositionerExtension() contains the code that changes the width of the positioner widget, so that CSS transition will catch that and provides animation.
All access to positioner widget in the class should be provided through updated methods.
3) onLeave() contains line removePositioner(getPositionerElement());
4) In the end of onMove() I added a couple of lines:
galleryWidget.extendHoveredRow(targetIndex - 1);
animatePositionerExtension();
where extendHoveredRow(hoveredWidgetOrdinal) implemented the logic to "limit" the sliding effect in the single line:
int rowHovered = -1;
public void extendHoveredRow(int hoveredWidgetOrdinal) {
int newRowHovered = getRowByOrdinalHovered(hoveredWidgetOrdinal);
if (rowHovered != newRowHovered) {
// adjust position of items in the previously hovered row
int firstInPreviouslyHoveredRow = (rowHovered - 1) * itemsInARow;
shiftFirstItemLeft(firstInPreviouslyHoveredRow, false);
rowHovered = newRowHovered;
// extend this row
int firstInThisRow = getOrdinalFirstInThisRowByOrdinal(hoveredWidgetOrdinal);
shiftFirstItemLeft(firstInThisRow, true);
}
}
This is in short how I did the thing. And still there's some room for improvements, like adding animated removal.
Again, it's all about overriding DropController and manipulations with elements inside the "gallery" widget. The benefit of this approach is that I remain in the gwt-dnd operations framework, and also reused a bunch of existent code.
Some notes:
CSS transition is not supported in IE pre-9, but this is unrelated to
this topic.
Put a transparent "glass" div on top of the Image widget if you use it
as a face of dragProxy. This will save you tons of time trying to
understand why either setting element's draggable to false, or
calling event.preventDefault() somewhere else, or other workarounds don't work in one or several browsers and the image itself is being dragged instead of the whole dragProxy widget.
Original goal:
I have a TreeMenu that i use to display my Menu.
In this tree, a user can select different items.
I would like to disable the tree, so that a user cannot select a new item after choosing the first.
The catch is, we cannot use setEnabled, because we are not allowed to use the greyed out look. The look/colors may not change.
Our proposed solution
Our first idea was to use a Composite with SWT.NO_BACKGROUND on top of the menu, to prevent any user interaction with the TreeMenu.
Code:
final Composite cover = new Composite(getPage().shell, SWT.NO_BACKGROUND);
cover.setLocation(getMenu().getLocation());
cover.setSize(getMenu().getSize());
cover.moveAbove(getMenu());
This has a problem with redrawing.
If the screen is covered by another screen and then brought back to front, the Cover Composite is filled with fragments of the previous overlapping window.
Our idea was to manually redraw the menu:
cover.moveBelow(getMenu());
getMenu().update();
cover.moveAbove(getMenu());
We placed the code inside the paintEventListener.
But this caused an infinite loop and did not solve the problem.
Questions
Does anyone have an idea how we can achive our orignial goal?
Does anyone know how we can make our proposed solution work?
Look at SWT-Snippet80. It shows how to prevent selections. A solution to your problem would be adding a listener like this to your tree:
tree.addListener(SWT.Selection, new Listener() {
TreeItem[] oldSelection = null;
public void handleEvent( Event e ) {
Tree tree = (Tree)(e.widget);
TreeItem[] selection = tree.getSelection();
if ( oldSelection != null )
tree.setSelection(oldSelection);
else
oldSelection = selection;
}
});
I wouldn't recommend trying to implement your workaround. I believe that placing transparent controls on top of each other is unsupported in SWT - I think I read a comment from Steve Northover on this subject once. Even if you made it work for some OS, it probably won't work in another - it's too much of a hack.
A solution that is supported by SWT, is having transparent windows on top of each other. But that is also really hard to implement (resizing, moving, always on top, redraw artifacts) and probably as big a hack as the other workaround. Go for the listener.