New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG?] scrollVisibleBool and alwaysTensileBool ignored #2525
Comments
I wanted to add a desktop specific package as I discussed here: https://www.codenameone.com/blog/scrollbars-tooltips.html but it didn't come to fruition. Did you add styling for the scrollbar after defining that? The problem is that scrollbars are often styled to be invisible so this code might help: https://www.codenameone.com/blog/permanent-sidemenu-getAllStyles-scrollbar-and-more.html you can probably use |
Thank you. I tried the code you suggested in https://www.codenameone.com/blog/permanent-sidemenu-getAllStyles-scrollbar-and-more.html, but it doesn't work correctly neither in the Simulator or in the Javascript built. It's easier to show you what it produces instead of describing the problems: This is the code I used:
with this
Moreover, this is only half of the problem: the other part is the impossible to disable |
This is newer code that does the same thing from the GUI builder/settings app: public static Component makeScrollable(final Component scrollable) {
if(!Display.getInstance().isDesktop()) {
return scrollable;
}
if (!(scrollable instanceof Container)) {
return scrollable;
}
ScrollBar scroll = new ScrollBar((Container)scrollable, ScrollBar.Y_AXIS);
Container sc = BorderLayout.center(scrollable).
add(BorderLayout.EAST, scroll);
$(sc).selectAllStyles().setBgColor(0xffffff).setBgTransparency(255);
return sc;
} Notice that in the simulator it should only work on the desktop skin. I'll assign this to 6.0 as 5.0 is winding down. @shannah I think we need a desktop package that includes better support for scrolling and related API's. |
I'm sorry, I didn't get where to put the new code, primarily because the |
Damn, I missed the fact that this isn't public. Anyway as a stopgap measure this is the class. Hopefully it doesn't have problematic dependencies: public class ScrollBar extends Container implements ScrollListener {
private final Container target;
private final DragHandle dragHandle;
private int orientation;
public static final int X_AXIS=0;
public static final int Y_AXIS=1;
public ScrollBar(Container target, int orientation) {
super(new LayeredLayout());
setScrollableX(false);
setScrollableY(false);
this.orientation = orientation;
this.target = target;
this.target.addScrollListener(this);
$(this).selectAllStyles()
.setBorder(Border.createCompoundBorder(Border.createEmpty(), Border.createEmpty(), Border.createLineBorder(1, 0xcccccc), Border.createLineBorder(1, 0xcccccc)))
.setBgColor(0xeaeaea)
.setBgTransparency(128)
.setPadding(0);
dragHandle = new DragHandle();
add(dragHandle);
LayeredLayout ll = (LayeredLayout)getLayout();
if (orientation == X_AXIS) {
ll.setInsets(dragHandle, "0px auto 0px "+getInsetForScroll(target.getScrollX())+"px");
} else {
ll.setInsets(dragHandle, getInsetForScroll(target.getScrollY())+"px 0.25mm auto 0.25mm");
}
}
int pressedX, pressedY;
@Override
public void pointerPressed(int x, int y) {
super.pointerPressed(x, y);
pressedX = x;
pressedY = y;
if (!dragHandle.contains(x, y)) {
int relY = y - getAbsoluteY();
int targetY = (int)Math.round((relY - getStyle().getPaddingTop()) * (target.getLayoutHeight() - target.getHeight()) /
((float)getInnerHeight() - dragHandle.getOuterPreferredH()));
if (target.getScrollY() > targetY) {
int newScrollY = target.getScrollY() - target.getHeight();
if (newScrollY < 0) {
newScrollY = 0;
}
target.scrollRectToVisible(target.getScrollX(), newScrollY, target.getWidth(), target.getHeight(), target);
} else if (target.getScrollY() < targetY) {
int newScrollY = target.getScrollY() + target.getHeight();
if (newScrollY > target.getLayoutHeight() - target.getHeight()) {
newScrollY = target.getLayoutHeight() - target.getHeight();
}
target.scrollRectToVisible(target.getScrollX(), newScrollY, target.getWidth(), target.getHeight(), target);
}
}
}
@Override
public void pointerDragged(int x, int y) {
super.pointerDragged(x, y);
if (!dragHandle.inDrag && !dragHandle.contains(x, y)) {
int relY = y - getAbsoluteY();
int targetY = (int)Math.round((relY - getStyle().getPaddingTop()) * target.getLayoutHeight() / ((float)getInnerHeight() - dragHandle.getOuterPreferredH()));
if (targetY < 0) {
targetY = 0;
}
if (targetY > target.getLayoutHeight() - target.getHeight()) {
targetY = target.getLayoutHeight() - target.getHeight();
}
target.scrollRectToVisible(target.getScrollX(), targetY, target.getWidth(), target.getHeight(), target);
}
}
@Override
protected Dimension calcPreferredSize() {
switch (orientation) {
case X_AXIS:
return new Dimension(target.getPreferredW(), px(2));
default:
int prefH = target.getPreferredH();
return new Dimension(px(2), prefH);
}
}
int lastTargetWidth, lastTargetLayoutWidth, lastTargetHeight, lastTargetLayoutHeight;
@Override
public void revalidate() {
if (lastTargetWidth != target.getWidth()
|| lastTargetHeight != target.getHeight()
|| lastTargetLayoutHeight != target.getLayoutHeight()
|| lastTargetLayoutWidth != target.getLayoutWidth()) {
lastTargetWidth = target.getWidth();
lastTargetHeight = target.getHeight();
lastTargetLayoutHeight = target.getLayoutHeight();
lastTargetLayoutWidth = target.getLayoutWidth();
forceRevalidate();
return;
}
super.revalidate();
}
@Override
public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) {
if (dragHandle.inDrag) {
// We already updated ourselves
return;
}
LayeredLayout ll = (LayeredLayout)getLayout();
LayeredLayoutConstraint cnst = ll.getOrCreateConstraint(dragHandle);
if (orientation == X_AXIS) {
cnst.left().setPixels(getInsetForScroll(scrollX));
} else {
int y = getInsetForScroll(scrollY);
if (y != cnst.top().getCurrentValuePx()) {
cnst.top().setPixels(y);
//System.out.println("Revalidating");
//forceRevalidate();
revalidate();
}
}
//revalidate();
}
private int getInsetForScroll(int scroll) {
LayeredLayout ll = (LayeredLayout)getLayout();
LayeredLayoutConstraint cnst = ll.getOrCreateConstraint(dragHandle);
if (orientation == X_AXIS) {
return (int)Math.round(scroll * getInnerWidth() / (float)target.getLayoutWidth());
} else {
int out = (int)Math.round(scroll * getInnerHeight() / (float)target.getLayoutHeight());
return Math.min(out, getInnerHeight() - dragHandle.getOuterPreferredH());
}
}
/*
private int convertScrollBarXToTargetX(int x) {
return (int)Math.round(x * target.getLayoutWidth() / (float)getWidth());
}
private int convertScrollBarYToTargetY(int y) {
return (int)Math.round((y + getStyle().getPaddingTop()) * target.getLayoutHeight() / ((float)getInnerHeight()));
}
private int convertTargetXToScrollBarX(int x) {
return (int)Math.round(x * getWidth() / (float)target.getLayoutWidth());
}
private int convertTargetYToScrollBarY(int y) {
return Math.max(0,
Math.min(getInnerHeight() - dragHandle.getOuterPreferredH(),
(int)Math.round(y * (getInnerHeight() - dragHandle.getOuterPreferredH()) / (float)target.getLayoutHeight())
));
}
*/
private class DragHandle extends Button {
int startOuterY, startOuterX;
int draggedX, draggedY, pressedX, pressedY;
int targetPressedScrollX;
int targetPressedScrollY;
boolean inDrag;
LayeredLayoutConstraint pressedConstraint;
DragHandle() {
RoundRectBorder border = RoundRectBorder.create()
.bezierCorners(false)
.cornerRadius(0.75f);
$(this).selectAllStyles().setBgColor(0xcccccc).setBgTransparency(200).setBorder(border)
.setPadding(0).setMargin(0);
this.setDraggable(true);
}
@Override
protected boolean isStickyDrag() {
return true;
}
@Override
protected Image getDragImage() {
return null;
}
@Override
protected void drawDraggedImage(Graphics g, Image img, int x, int y) {
}
@Override
protected int getDragRegionStatus(int x, int y) {
return orientation == X_AXIS ? Component.DRAG_REGION_IMMEDIATELY_DRAG_X : Component.DRAG_REGION_IMMEDIATELY_DRAG_Y;
}
@Override
protected void dragFinished(int x, int y) {
super.dragFinished(x, y); //To change body of generated methods, choose Tools | Templates.
inDrag = false;
}
@Override
public void pointerPressed(int x, int y) {
super.pointerPressed(x, y);
inDrag = true;
targetPressedScrollX = target.getScrollX();
targetPressedScrollY = target.getScrollY();
startOuterY = dragHandle.getOuterY();
startOuterX = dragHandle.getOuterX();
pressedX = x;
pressedY = y;
LayeredLayout ll = (LayeredLayout)ScrollBar.this.getLayout();
pressedConstraint = ll.getOrCreateConstraint(this).copy();
pointerDragged(x, y); // so drag starts instantly
}
@Override
public void pointerDragged(int x, int y) {
super.pointerDragged(x, y);
setVisible(true);
draggedX = x;
draggedY = y;
int deltaX = draggedX - pressedX;
int deltaY = draggedY - pressedY;
if (startOuterY + deltaY < ScrollBar.this.getStyle().getPaddingTop()) {
deltaY = ScrollBar.this.getStyle().getPaddingTop() -startOuterY;
}
if (startOuterX + deltaX < ScrollBar.this.getStyle().getPaddingLeft(false)) {
deltaX = ScrollBar.this.getStyle().getPaddingLeft(false)-startOuterX;
}
if (startOuterY + dragHandle.getOuterPreferredH() + deltaY > ScrollBar.this.getInnerHeight()) {
deltaY = ScrollBar.this.getInnerHeight() - dragHandle.getOuterPreferredH() - startOuterY;
}
if (deltaY == 0 && orientation == Y_AXIS) {
return;
}
if (deltaX == 0 && orientation == X_AXIS) {
return;
}
if (pressedConstraint == null) {
return;
}
LayeredLayoutConstraint cnst = pressedConstraint.copy();
switch (orientation) {
case X_AXIS:
cnst.left().translatePixels(deltaX, true, ScrollBar.this);
break;
default:
cnst.top().translatePixels(deltaY, true, ScrollBar.this);
}
LayeredLayout ll = (LayeredLayout)ScrollBar.this.getLayout();
cnst.copyTo(ll.getOrCreateConstraint(this));
ScrollBar.this.revalidate();
// Set the target's scroll position
switch (orientation) {
case X_AXIS:
//target.scrollRectToVisible(targetPressedScrollX + convertScrollBarXToTargetX(deltaX), targetPressedScrollY, target.getLayoutWidth(), target.getLayoutHeight(), target);
break;
default:
int targetDeltaY = Math.round(deltaY * (target.getLayoutHeight() - target.getHeight())/((float)ScrollBar.this.getInnerHeight()-getOuterPreferredH()));
int targetY = targetPressedScrollY + targetDeltaY;
if (targetY + target.getHeight() > target.getLayoutHeight()) {
targetY = target.getLayoutHeight() - target.getHeight();
}
target.scrollRectToVisible(targetPressedScrollX, targetY , target.getWidth(), target.getHeight(), target);
}
}
@Override
protected Dimension calcPreferredSize() {
int lHeight = target.getLayoutHeight();
int iHeight = target.getHeight();
if (iHeight == 0 || lHeight == 0) {
//it hasn't been laid out yet.. lets wait and revalidate later
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
$(()->{
DragHandle.this.setShouldCalcPreferredSize(true);
ScrollBar.this.revalidate();
});
}
}, 30);
return new Dimension(0, 0);
}
if (orientation == X_AXIS) {
int h = Math.max(px(4), ScrollBar.this.getInnerHeight());
if (target.getLayoutWidth() == target.getWidth() || !target.isScrollableX()) {
return new Dimension(0,0);
}
int w = (int)Math.round(ScrollBar.this.getInnerWidth() * ((float)target.getWidth()/target.getLayoutWidth()));
return new Dimension(w, h);
} else {
int w = ScrollBar.this.getInnerWidth();
if (target.getLayoutHeight() == target.getHeight() || !GBAccessor.scrollableYFlag(target)) {
return new Dimension(0,0);
}
int h = (int)Math.round(ScrollBar.this.getInnerHeight() * ((float)target.getHeight()/target.getLayoutHeight()));
return new Dimension(w, h);
}
}
}
private int px(double mm) {
return Display.getInstance().convertToPixels((float) mm);
}
} |
Thank you. The only external dependency is in this line, about the static method
|
I think you can just remove this but @shannah correct me if I'm wrong |
I seem to recall it being necessary. You can get it almost working correctly by changing I'm trying to decide whether we should expose the The difference is that isScrollableY() will return a different value depending on the state of the UI (e.g. if the component is scrollable, but it currently doesn't need to scroll, then |
Thank you both, however the result in a test case is not fine with or without The scroolbar doesn't seem usable in both cases: it seems a vertical line of the same color (without an inner small line like in the browser scrool bar) that occupies all the height space, so there is no indication of the scrolling. Please also note that the scrolling with the mouse wheel is quite fine on Chrome, but it's quite impossible with Firefox (because the page scrolls very slowly). Tested on Linux. Can you reproduce this problem with the two links in this comment? So, at the moment there are three issues: I'm not able to add a scroolbar, I'm not able to disable the alwaysTensile effect, and scrolling with mouse wheel seems problematic with Firefox. I hope for a solution for all :) |
Please post the code for your test case. |
The test case is composed by these three files:
|
It could be because of your nested scrollables. Change the form layout to BorderLayout, then add the scrollable container into its center. That will probably fix it. |
Thank you very much, But it still works badly on Firefox (tested on Linux and Windows). I also noted that in the following line of So, at the moment, the result in Chrome is satisfying. About Firefox, the "too much slow scrolling" with the mouse wheel happens also without the scrollbar... and if we add the scrool bar it is however "difficult to move". This is the new code of the test case after the last changes:
|
First observation. Firefox on Mac works fine. |
I have made a few changes the scrolling issue in Firefox. Tested on FF Windows 10. They will be available in the next server update. |
Thank you very much :) Does I have to file another issue for the "alwaysTensileBool" impossible to deactivate, already reported from the starting of this discussion? |
You need to set both alwaysTensileBool=false and tensileDragBool=false (Actually I'm not sure if the former is even necessary, but tensileDragBool=false is necessary) |
Thank you, setting both alwaysTensileBool=false and tensileDragBool=false the issue is solved! |
Note: Upon further testing of my latest changes, there still may be performance issues in Firefox on Windows. It's finicky. (It seems to work better with developer tools opened, than with it closed - which is very strange). On my Surface Pro, the scrolling in Firefox is sporadically choppy and slow. It works well on all other platforms (FF/OSX, FF/Linux, Chrome/OSX, Chrome/Linux, Chrome/Windows, Edge/Windows) all scroll smoothly and consistently. Running FF/Windows 10 on VirtualBox on my Ubuntu machine seems to work OK so it might just be my Surface. The issue seems similar to react-dnd/react-dnd#1000 in the sense that it seems to randomly lose mouse events (they never get delivered). When the update is out on Friday, please try it out and see if it fixes it for you. |
@shannah Ok. Thank you for your effort, on Friday I'll do the test. |
Today I tested the scrolling on Firefox and Chrome on Linux. It seems fine. At the moment, I cannot test my project ported to Javascript on different operating systems, I'll do that in a next step. About the performance issues, yes, I have performance issue on Firefox (I haven't them on Chrome), but I cannot know if they are related to the scrolling or not. I suppose not. The performance penality, in my case, means that the browser reacts as more slowly to the button clicks (to add or cancel sorted rows) as more data I have in a scrollable container containing a Table binded to |
Closing this issue. If firefox performance continues to be a problem, let's open a new issue for that - hopefully with a test case that we can try to optimize. |
In a project that targets Javascript I want that the scrollbars are always visibile with a decent width (if there is a scrollable content) and I don't want the "always tensile" effect of iOS. Or, at least, I want to be able to choose when to show scroll bars. The problem is that the theme constant
scrollVisibleBool
andalwaysTensileBool
are ignored. I suppose that this issue is not specific of the Javascript port because it happens also in the Simulator (tried with iPhone and iPad skins).I tried to add these constants in the CSS, but there is no effect:
I also tried
.setScroolVisibile(true)
on some container with scrollable content, without any effect.As last resource, I tried this approch in the
init()
, without effect:For this issue, a very simple test case can be:
with this CSS:
I used the standard updated Codename One libs, without compiling against the Codename One sources.
A final consideration: if the browser (because I target to Javascript) is used from a touch screen device, the default of no scrool bar is ok, but if it's used from a standard browser of a computer desktop (that means "no touch screen" and the use of the mouse) the user expects to see the scroolbars and to be able to use both the scrool wheel and the scrool bars... moreover, the alwaysTensileBool in a desktop browser is very strange.
The text was updated successfully, but these errors were encountered: