Suppose I have a custom slider component, many of those, in a BoxLayout.Y-scrollable-Y Container. The custom slider components cover all of the container.
What I want is that the slider components handle pointerDragged in the X-Axis while still having the Container beeing draggable in order to scroll the "list" just as if there where no slider components at all.
The only way I can think of to achieve this seems to be a hack. I'd do that by overriding some methods in the slider components in a way that it would forward pointer event to its Container like this:
#Override
protected int getDragRegionStatus(int x, int y) {
return Component.DRAG_REGION_LIKELY_DRAG_XY;
}
#Override
public void pointerDragged(int x, int y) {
super.pointerDragged(x, y);
{
// Handle X-axis dragging here
}
boolean focusable = getParent().isFocusable();
try { // handle Y-axis dragging there
getParent().setFocusable(true);
getParent().pointerDragged(x, y);
} finally {
getParent().setFocusable(focusable);
}
}
I doubt that this is the way this should be done. But just as a wrote above this is the only way I can think of how to make it work with Codename One.
But how would You get this working?
Why is it a hack?
You're trying to achieve a special case, you can do it by overriding the form pointer events or binding a listener to the form itself.
Related
I have a 3d scene built with react threefibre and I'm able to zoom in and out with both mousewheel and a DOM eleemnt zoombar in this scene.
the demo of the scene can be seen here:
https://codesandbox.io/s/zooming-using-multiple-inputs-lub391
right now, when you zoom with mousewheel, zoombar stays the same, but I want the it change dependant on the mousewheel.
note that I don't want to use mouse wheel event because I may have more sources that also change the zoom and I want the zoombar to be dependant on them too. so the proper way is to add an event listener on camera movement. I wasn't able to do that because whatever I do makes an endless loop.
Perhaps you could use an EventListener on the identified Canvas that listens for mousewheel events like so:
useEffect(() => {
document.getElementById('map-canvas')?.addEventListener('mousewheel', onMouseWheel, false)
return () => {
document.getElementById('map-canvas')?.removeEventListener('mousewheel', onMouseWheel, false)
}
}, [])
return (
<Canvas id="map-canvas"></Canvas>
)
The onMouseWheel function adds the desired value to the previously created context.
I have a scrollabillity issue in a Form, layered out by BoxLayout.y().
The form contains many Tabs (with fixed size), each tab of the Tabs can contain an image or a video (the video is inside a BorderLayout to scale it at the tab size).
If an image is shown, the scrolling up and down works correctly.
If a video is shown, the y scrolling is not possibile, I can only swipe to change the tab.
I suppose that the cause of this issue is that videos are native component (I used the Codename One API to show the videos).
How can I solve or workaround this issue? This is crucial for the app design. Thanks for the tips.
The video.setEnabled(false) workaround (Make videos scrollable) doesn't work.
I workaround in a different way, inserting the MediaPlayer container in a LayeredLayout container, and then placing a Button over the MediaPlayer. A generic empty Label doesn't work, but an empty Button works. Of course I have to override calcPreferredSize to make the MediaPlayer and the Button of the same size (or use a different approach to make them of the same size).
This allows scrolling, but prevents the tapping of the play and pause Buttons (that are over the video). I solved also this issue.
In short, this is my solution, tested on Simulator, Android and iOS (in the following code, note that videoBtnsCnt is a Container over the video, in which I inserted play and pause Buttons):
MediaPlayer mediaPlayer = new MediaPlayer(video) {
#Override
public Dimension getPreferredSize() {
return new Dimension(size, size);
}
};
Container mediaPlayerCnt = new Container(new LayeredLayout(), "NoMarginNoPadding") {
#Override
public Dimension getPreferredSize() {
return new Dimension(size, size);
}
};
mediaPlayerCnt.add(mediaPlayer);
Button allowScrollingLabel = new Button() {
#Override
public Dimension getPreferredSize() {
return new Dimension(size, size);
}
};
allowScrollingLabel.setUIID("NoMarginNoPadding");
allowScrollingLabel.addActionListener(l -> {
Component responder = videoBtnsCnt.getResponderAt(l.getX(), l.getY());
if (responder instanceof Button) {
// it can be a play button or a pause button
((Button) responder).pressed();
((Button) responder).released();
}
});
mediaPlayerCnt.add(allowScrollingLabel);
With Codename One components can change their size and this even can be animated, which is nice.
But what is the expected behaviour with shrinking Container instances, specifically if they are scrollable?
Having a Container, scrollable in the Y-axis and content that is smaller than the container the content sticks to the top. The container can be dragged and the content scrolls back to the top of the container. However, when the content shrinks and becomes smaller than the container the behaviour is - somewhat strange.
I have made an example - when the coloured components are tapped they expand. If tapped again they shrink. One can tap the green Label and it expands. Tapping it again it shrinks and everything is as of before.
However, if one taps a coloured label and scrolls any amount down before tapping it again to shrink it this is the resulting view:
Only if one then drags the scrolling container ever so lightly then the contant scrolls back to the top.
I assume this is a bug. Though I wonder - what should be the behaviour when the content shrinks beyond the scrolling containers size? Since the user might expect the component just tapped to remain where it was - can the scrolling behaviour be controlled somehow?
This is just an example but I want to build expandable "drawer" components.
Here is the code:
public class FormContentSmallerScrollableContainer extends Form {
private class LabelExpandable extends Label {
boolean expanded = false;
LabelExpandable(String aTitle, int aColor) {
super(aTitle);
getAllStyles().setBgPainter((aGraphics, aRectangle) -> {
aGraphics.setColor(aColor);
aGraphics.fillRoundRect(getX(), getY(), aRectangle.getWidth() - 1, aRectangle.getHeight() - 1, 20, 20);
});
setOpaque(true);
}
#Override
public void pointerReleased(int x, int y) {
super.pointerReleased(x, y);
expanded = !expanded;
setShouldCalcPreferredSize(true);
getParent().animateLayout(400);
}
#Override
protected Dimension calcPreferredSize() {
if (!expanded) {
return super.calcPreferredSize();
}
Dimension dimension = super.calcPreferredSize();
dimension.setHeight(Display.getInstance().getDisplayHeight());
return dimension;
}
}
public FormContentSmallerScrollableContainer() {
super("FormContentSmallerScrollableContainer");
setScrollable(false);
setLayout(new BorderLayout());
add(BorderLayout.NORTH, new Label("The container below is scrollable in the y-axis"));
Container containerScrollable = BoxLayout.encloseY(
new LabelExpandable("LabelExpandable green", 0x00ff00),
new LabelExpandable("LabelExpandable blue", 0x00c0ff));
containerScrollable.setScrollableY(true);
add(BorderLayout.CENTER, containerScrollable);
add(BorderLayout.SOUTH, new Label("The container above is scrollable in the y axis"));
}
}
This is a long standing behavior when we animate a scrollable area to what's effectively a non-scrollable area. We tried working around this in the past but couldn't find the right approach. You can file the issue on this but I'm not optimistic as it's really hard to even find a workaround for this.
I need to implement a complex feature with Codename One that involves the moving of a tapped component (like a Button) on the screen: the users will have some buttons on the layered pane and they should be able to move them using the finger. Abstracting my problem to make it as more general as possible, these are the requirements:
there are several buttons in the layered pane;
each button has an initial position calculated by an algorithm that needs to know the screen size;
the position of each button can be inside the visible part of the screen or out of it (for example the x/y position can be negative or bigger than the screen size);
if the user only taps a button, its action listener should be invoked...
... instead, if the user taps a button and moves the finger while continuing to press the screen, my algorithm should receive the x/y axis movement of the finger in real time and it should be able to update the position of all buttons while the finger moves.
It's not a game. Here I abstracted the problem to adapt my requirements to several situation. How can I implement these things in Codename One?
A "simple" use case, to better understand what I mean, is for example the moving of buttons disposed in a circle: in this example, the user can tap a single button or he/she can rotate all the circle of buttons moving the finger while tapping a button.
You can use setDraggable(true) and make the drop containers into a droppable target using setDropTarget(true). Once you do that the default behavior of Container will allow you to visually rearrange/move components around between droppable Container instances. You can simply override the default drop method in Container with something smarter that implements the functionality you want:
public void drop(Component dragged, int x, int y) {
int i = getComponentIndex(dragged);
if(i > -1) {
Component dest = getComponentAt(x, y);
if(dest != dragged) {
int destIndex = getComponentIndex(dest);
if(destIndex > -1 && destIndex != i) {
setComponentIndex(dragged,destIndex);
}
}
animateLayout(400);
} else {
Container oldParent = dragged.getParent();
if(oldParent != null) {
oldParent.removeComponent(dragged);
}
Component pos = getComponentAt(x, y);
i = getComponentIndex(pos);
if(i > -1) {
addComponent(i, dragged);
} else {
addComponent(dragged);
}
getComponentForm().animateHierarchy(400);
}
}
I have a requirement to change the color of the scroll bar based on some user selection.
In a typical form, I have switch case where is I am setting the scroll bar color using:
#Override
protected void beforeTopic(final Form f) {
int scrollColor=0x000000;
switch(userSelectedTopic)
{
case 1:
scrollColor=0x59be8a;
break;
case 2:
scrollColor = 0xff3333;
break;
.
.
.
}
// setting color to scroll thumb
Style s = UIManager.getInstance().getComponentStyle("ScrollThumb");
System.out.println(scrollColor);
s.setFgColor(scrollColor);
s.setBgColor(scrollColor);
s.setBgTransparency(255);
UIManager.getInstance().setComponentStyle("ScrollThumb", s);
s = UIManager.getInstance().getComponentStyle("ScrollThumb");
System.out.println("-->>"+s.getFgColor());
}
What happens is that the color code is picked properly for the first time.
When this form is invoked again, with a different user selection, the color code value changes as per the switch statement. The style attributes also change.
However, the initial color applied to the thumb prevails!
What could be the issue?
I tried f.refreshTheme(); but this does not seem to work. It just sustains the first applied color
That won't work. getComponentStyle will always return a copy which is the way it should be. Otherwise when you do something like getUnselectedStyle().setFgColor() you will change the color of all the components.
One way to do that is to update the theme, you can define another theme that just sets the color of the scroll then apply it using addThemeProps of the UIManager and then invoke refreshTheme() on the form.
The alternative is to derive the scrollable component and paint the scroll yourself or hide the scroll entirely and paint it yourself using the glasspane or something like that.