Skip to content
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

Make overriding VirtualizedScrollpane's layoutChildren() more developer-friendly #14

Closed
JordanMartinez opened this issue Nov 24, 2015 · 6 comments

Comments

@JordanMartinez
Copy link
Contributor

Coming from FXMisc/RichTextFX#205 and relating to #9, provide more access to VirtualizedScrollPane's objects so that layoutChildren can be better overridden.

There's a few ways this could be done:

  • Complete freedom: make the (currently private) content, hbar, and vbar protected. Then the developer can fully layout the children as they desire.
  • Restrict to content layout: since a developer will need to rewrite the scroll bar implementation each time he/she extends VirtualizedScrollPane (or more probably, screw up how the scroll bars get laid out), use a protected method to layout the content which developers can override:
protected void layoutChildren() {
    // normal layout code...

    // instead of "content.resize(w, h)" have
    layoutContent(w, h);

   // rest of method's code.
}

protected void layoutContent(double width, double height) {
    content.resize(width, height);
}
@JordanMartinez
Copy link
Contributor Author

As discussed in Pull Request 15, implementing the idea above does not adhere to composition over inheritance (See this article for more info).

So, to change the way that content is laid out, the content should be wrapped in some intermediate class that is then the content within VirtualizedScrollPane. For example...

Virtualized actualContent = // creation code
ScaledVirtualized wrapper = new ScaledVirtualized(actualContent);
VirtualizedScrollPane<ScaledVirtualized> vsPane = new VirtualizedScrollPane(wrapper);

where ScaledVirtualized scales actualContent when user scrolls up/down and the control key is presssed (written in Groovy):

class ScaledVirtualized<V extends Node & Virtualized> extends Region implements Virtualized {
    final V content
    Scale scale = new Scale(1, 1, 0, 0)

    ScaledVirtualized(V content) {
        super()
        this.content = content
        getChildren().add(content)
        getTransforms().add(scale)
        addEventFilter(ScrollEvent.SCROLL, { ScrollEvent event ->
            if (event.controlDown) {
                scale.with {
                    if (event.deltaY > 0) {
                        y *= 0.8
                        x *= 0.8
                    } else {
                        y /= 0.8
                        x /= 0.8
                    }
                }
                layoutChildren()
                event.consume()
            }
        })
    }

    @Override
    protected void layoutChildren() {
        double width = layoutBounds.width
        double height = layoutBounds.height
        content.resize(width / scale.x, height/ scale.y)
    }
    @Override
    Var<Double> estimatedScrollXProperty() {
        content.estimatedScrollXProperty()
    }
    @Override
    Var<Double> estimatedScrollYProperty() {
        content.estimatedScrollYProperty()
    }
    @Override
    Val<Double> totalHeightEstimateProperty() {
        content.totalHeightEstimateProperty()
    }
    @Override
    Val<Double> totalWidthEstimateProperty() {
        content.totalWidthEstimateProperty()
    }
}

@JordanMartinez
Copy link
Contributor Author

After doing the above, I came across a small issue. When one "zooms the area out," so that the content's letters are very small and it fits inside the entire viewport, the scroll bars no longer need to be visible, but still are.

@JordanMartinez
Copy link
Contributor Author

It's clear that I need to override the totalWidth/HeightEstimateProperties somehow. I'm still figuring out how to do it correctly.

Edit: Figured out how to fix scroll bar issue. For others who might want something similar, the following code will display scroll bars when needed regardless of scale value (written in Groovy):

class ScaledVirtualized<V extends Node & Virtualized> extends Region implements Virtualized {
    final V content
    Scale scale = new Scale(1, 1, 0, 0)
    Val<Double> estHeight
    Val<Double> estWidth
    Var<Double> estScrollY
    Var<Double> estScrollX

    ScaledVirtualized(V content) {
        super()
        this.content = content
        getChildren().add(content)
        getTransforms().add(scale)
        estHeight = Val.combine(
                content.totalHeightEstimateProperty(),
                scale.yProperty(),
                { estHeight, scaleFactor -> estHeight * scaleFactor }
        )
        estWidth = Val.combine(
                content.totalWidthEstimateProperty(),
                scale.xProperty(),
                { estWidth, scaleFactor -> estWidth * scaleFactor }
        )
        estScrollX = Var.mapBidirectional(
                content.estimatedScrollXProperty(),
                (Function) { double scrollX -> scrollX * scale.x },
                (Function) { double scrollX -> scrollX / scale.x }
        )
        estScrollY = Var.mapBidirectional(
                content.estimatedScrollYProperty(),
                (Function) { double scrollY -> scrollY * scale.y },
                (Function) { double scrollY -> scrollY / scale.y }
        )
        addEventFilter(ScrollEvent.SCROLL, { ScrollEvent event ->
            if (event.controlDown) {
                scale.with {
                    double factor = 0.9
                    if (event.deltaY > 0) {
                        y *= factor
                        x *= factor
                    } else {
                        y /= factor
                        x /= factor
                    }
                }
                layoutChildren()
                event.consume()
            }
        })
    }

    @Override
    protected void layoutChildren() {
        double width = layoutBounds.width
        double height = layoutBounds.height
        content.resize(width / scale.x, height/ scale.y)
    }
    @Override
    Var<Double> estimatedScrollXProperty() {
        estScrollX
    }
    @Override
    Var<Double> estimatedScrollYProperty() {
        estScrollY
    }
    @Override
    Val<Double> totalHeightEstimateProperty() {
        estHeight
    }
    @Override
    Val<Double> totalWidthEstimateProperty() {
        estWidth
    }
}

@TomasMikula
Copy link
Member

Nice, I'm glad that it works!

@TomasMikula
Copy link
Member

If you feel like it, you can contribute this ScaledVirtualized to Flowless. Maybe without the zooming by scroll functionality and with public scaleProperty(), so that anyone can control the scaling however they want.

@JordanMartinez
Copy link
Contributor Author

Good point. That would actually be better since others would already have a base from which to extend or to implement other functionality specific to their needs.

I've created a PR for that in Flowless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants