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

Scroll viewport to end on first render #170

Closed
goodsoft opened this issue Apr 18, 2018 · 9 comments
Closed

Scroll viewport to end on first render #170

goodsoft opened this issue Apr 18, 2018 · 9 comments

Comments

@goodsoft
Copy link

I seem to have a following problem:

  1. I have a log output widget in viewport, which isn't rendered at all until user makes a certain action.
  2. Even before first render log messages are being pushed into this widget state.
  3. When each message is pushed, I trigger widget cache invalidation and call vScrollToEnd.
  4. When the widget is displayed for the first time, it is not scrolled to bottom, but begins properly scrolling after the next log message arrives.

In other words, scroll requests get lost if they happen before widget is rendered for the first time.

Am I doing something wrong?
If not, is there some way to get the viewport scrolled to the bottom during the very first render?

Thank you.

@jtdaugherty
Copy link
Owner

Scrolling requests made during an EventM handler only affect the rendering immediately following that event handler. So if you make a scrolling request and the UI doesn't even render the viewport in question, the scrolling request has nothing to scroll, since the scrolling request has to be relative to the current viewport state and there isn't any.

It seems to me that the right time to make an "initial" vScrollToEnd call is when the "user makes a certain action" that you mentioned above. Is that possible? Another way to think about this is that your log state in your application is separate from the viewport state that is managed by Brick. The point at which you start drawing a viewport from your own state is the the point at which you have to start managing the viewport state with scrolling calls.

@goodsoft
Copy link
Author

goodsoft commented Apr 18, 2018

I'm trying to encapsulate this functionality into widget as much as possible, and this solution unfortunately leaks implementation details into app global event handling code.
If we have different ways of getting to this widget, each of them will have to "initialize" widget before switching to it.

The point at which you start drawing a viewport from your own state is the the point at which you have to start managing the viewport state with scrolling calls.

The thing is, currently I need to start managing viewport state just before I start drawing it.
And by the time I get to widget's "internal" drawing code, it's already too late to issue scrolling requests.

Better solution, in my opinion, would be somehow passing initial scrolling request as a combinator to Brick.viewport.

@jtdaugherty
Copy link
Owner

If we have different ways of getting to this widget, each of them will have to "initialize" widget before switching to it.

I don't know what you're doing in your application, of course, but presumably if you have multiple ways to "get to" this widget then there is already a place to put code like this, e.g.,

showMyWidget :: s -> EventM n s
showMyWidget s = do
  -- Set whatever application state flag that determines whether the widget gets shown
  -- Issue initial scroll request here

I can definitely appreciate the desire to avoid leaking details like this into the main event handler, but on the whole I don't think it's possible to avoid that forever with Brick since it requires you to slice your application up to separate drawing from event handling anyway.

Still, this is one of the reasons why I suggested the approach that I did in #169; viewports are great when they're a perfect fit for what you need to do, and beyond that they can be tricky to use correctly. The performance issue alone is enough of a reason not to use viewports, and your desire to always render the log "at the bottom" is another case where using viewports to indirectly get the behavior you want can be awkward.

@goodsoft
Copy link
Author

Okay, maybe I'm still not completely grasping your idea of where using viewport is a good idea.

Anyway, one more case, when I would want to issue scroll request during rendering, is the moment of launching app, i.e. when no event has arrived at all yet.
Consider text input field, which scrolls horizontally, and which I would want to be filled, focused and scrolled to the end of the line from the very start.

@jtdaugherty
Copy link
Owner

Anyway, one more case, when I would want to issue scroll request during rendering, is the moment of launching app, i.e. when no event has arrived at all yet.

For that, see appStartEvent.

@simonmichael
Copy link
Contributor

simonmichael commented Nov 19, 2021

I have the same issue. I'd like to keep using a viewport, and I'm not quite seeing how to apply your comments above.

In hledger-ui I navigate from the accounts screen to an account's register screen here, using helpers
asEnterRegister,
rsCenterAndContinue,
scrollSelectionToMiddle,
screenEnter.

scrollSelectionToMiddle has no effect the first time through, since it finds no viewport for the new screen's list, which has not been rendered yet. @jtdaugherty, if any obvious solution jumps out at you do let me know.

@simonmichael
Copy link
Contributor

I see it's easy to call the draw function from an event handler. I thought this might do the trick, but I think it's probably not forcing enough evaluation (and Widget doesn't have the right instances for deepseq):

rsCenterAndContinue ui = do
  traceShow (length $ rsDraw ui) $  -- force a render in case this is the first time, to ensure there's a viewport we can scroll
    scrollSelectionToMiddle $ rsList $ aScreen ui
  continue ui

@jtdaugherty
Copy link
Owner

@simonmichael I'm not sure I understand the problem you're facing. Would you be willing to file a separate ticket with a bit more description of what you want to accomplish? (I'm find it hard to tell whether your problem is the same as the one in this ticket.)

@jtdaugherty
Copy link
Owner

(Also, for posterity, calling your drawing function in the event handler will have no effect whatsoever on the drawn state of the screen.)

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

3 participants