Is there way to implement mouse wheel listener for ScrollPane? I looked at couple of options:
Viewport does not seem to have mouse wheel listener
Played with gef's MouseWheelHelper but the y value only changes when I click within the ScrollPane, not when mouse wheel is scrolled. Although event is being fired.
Also, there is no vertical scroll bar for the ScrollPane by design.
Ok, the magic number here is org.eclipse.swt.widgets.Event.count. This number tells you "number of lines or pages to scroll using the mouse wheel". Combining this and MouseWheelHelper interface I was able to make the scroll with mouse wheel work:
public class MyEditPart extends TreeEditPart implements MouseWheelHelper{
private static final int SCROLL_OFFSET = 10;
...
...
#Override
public void handleMouseWheelScrolled(Event event) {
pane.scrollVerticalTo(pane.getViewport().getVerticalRangeModel().getValue() + (event.count * SCROLL_OFFSET));
}
}
What's neat is that by changing SCROLL_OFFSET I can control the speed with which Viewport scrolls.
The only thing to check is definition of Event.count that says:
depending on the event type, the number of following
paint events that are pending which may always be zero
on some platforms, or the number of lines or pages to
scroll using the mouse wheel, or the number of times the
mouse has been clicked
Not sure how this will play out on other OSes.
Related
I have a listview and a rectangle on top of it. The ListView's delegates have DropAreas in them and the rectangle has drag enabled on Y axis with its relevant properties (hotspot, target).
When I move the rectangle up and down on the ListView, the DropAreas of the delegates are registering onEntered signals. Working as intended.
However, I am using SmoothedAnimation to scroll the list up and down when the rectangle is at the most top and bottom coordinates (list.y = 0 and list.height). So the rectangle is not moving, and the DropAreas of the list's delegates are moving under it. In that case, the onEntered is not registered because there is no dragging, the rectangle is completely still and although its hotspot is entering and leaving the DropAreas, there is no interaction.
This is because dragging mechanic is sending events all the time when moving and any DropAreas it comes inside can register the event. In my case there is no dragging and therefore no events.
Question: Can drag events be manually activated? Can I somehow simulate drag?
At first, you should change the drag.target: parent to drag.target: this. In this way instead of dragging the parent item, you drag only the mouseArea. After that, you should grab an image from the item that you want to drag. The code is here:
Rectangle {
id: rec
width: 100
height: 100
Drag.active: dragArea.drag.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: this
onPressed:{
parent.grabToImage(function(result) {
parent.Drag.imageSource = result.url
})
}
}
}
I have managed to induce drag events by manually changing the hotSpot property of the rectangle above the list. QML Drag documentation says:
Changes to hotSpot trigger a new drag move with the updated position.
So I have done this every time Listview contentY changes (vertical scrolling happens)
onContentYChanged:
{
rectangle.Drag.hotSpot.x += 0.0001
rectangle.Drag.hotSpot.x -= 0.0001
}
In hindsight, however, this is a 'hacky' solution. hotSpot property is a QPointF class property (link). This means it can be set using various methods (setX, setY, rx, ry..). For example, this:
rectangle.hotSpot += Qt.point(0,0)
or
rectangle.hotSpot = Qt.point(rectangle.hotSpot.x,rectangle.hotSpot.y)
should work in theory by resetting the hotSpot point when contentY changes, but testing revealed it unfortunately does not trigger a new drag move. (bug or intended, I don't know)
Now some of you that are more into Qt and QML might know more about this and how to properly adress the issue, but the above solution works for me and after testing everything I could imagine to get a cleaner solution, I settled on it.
I have a simple application written with SWT. I'm geting GC from SWT.Paint event. In my main canvas I've added a MouseMovelistener so some text will be displayed over specified area if mouse is in area.
I would like to remove text from GC after the mouse is not anymore over area. I didnt find any mention about removing drawn objects from GC or replacing GC with new one.
Could you tell me how can I can achive such result? There is no other access to GC object than through SWT.Paint event.
Elements like text of lines that are drawn on a GC cannot be removed. Even though, a GC has methods to draw text and lines, etc, these methods merely transform the shapes into pixels and that is all a GC knows of.
Therefore your application should maintain a model that allows to position text and remove once placed text elements. Whenever the model changes, the canvas should be refreshed with canvas.redraw() which sends an SWT.Paint event. Your paint listener can then examine the model and paint text accordingly.
The article Graphics Context - Quick on the draw has further details on SWTs graphics context.
Add MouseTrackListener and control mouse enter and leave canvas
MouseTrackListener mtl = new MouseTrackAdapter() {
#Override
public void mouseEnter(MouseEvent e) {
// add your PaintListener here
}
#Override
public void mouseExit(MouseEvent e) {
// remove PaintListener here
}
};
canvas.addMouseTrackListener(mtl)
I have a UIpanel in my application and i instantiate gameobject in it programmatically during runtime, the problem i have is i have to create more than 1000 gameobjects (sometimes up to 6000) in that panel and that takes a long time freezing the UI and trying it with Coroutines cause some lags as well.
is there any way to have a list of gameobjects and create them in UI panel during user scrolling that panel?
or can somebody suggest a better way of adding gameobjects to a panel without any lag or UI freeze?
UPDATE :
I added this to clear my goal.
i have a long list of items that i trying to put them in a list each item have a picture a some button and some texts, below there is a sample from a single item:
now i need to instantiate this complex sample in a panel and each item change itself based on a ID i pass after instantiation.
but i have a panel capacity problem and even if i cut some item when i put that many object in a panel at once scrolling in panel make it very slow and painful
also i need a scroll bar for my panel so i have to have all of my object available in the panel
hope this information helped.
When you describe it that way, its much clearer & much simpler.
Look at any mobile application: they do not put 6000 people records on a single page. Its normally paged or grouped, with a search option. So do that instead.
If you look at the facebook mobile app (a good standard) if you start scrolling to look at older posts, every few seconds it pauses for a second or two to load the next few posts. Its the same with the youtube app.
Because of this 65535 unity limit, just put in a paging limit (say 100-200 items).
Ok, I just wrote a test program which generated 1000 Text UI elements.
public class MakeLoadsOfUI : MonoBehaviour {
public GameObject prefab;
// Use this for initialization
void Start ()
{
MakeUI();
}
void MakeUI()
{
float startTime = Time.realtimeSinceStartup;
for (int i = 0; i < 1000; i++)
{
int x=i/40, y=i%40;
GameObject obj = (GameObject)Instantiate(prefab);
obj.transform.SetParent(transform); // attack to the canvas
RectTransform rt = obj.GetComponent<RectTransform>();
rt.anchoredPosition = new Vector2((x-10)*25, (y-10)*20);
obj.GetComponent<Text>().text = "UI:" + i;
}
float endTime = Time.realtimeSinceStartup;
Debug.Log("time taken:" + (endTime - startTime));
}
}
It took 0.15 seconds for 1000 elements which was quite reasonable. If you had a different result please elaborate, explaining how you did it.
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.