-
Notifications
You must be signed in to change notification settings - Fork 26
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
Improve Layout #36
Improve Layout #36
Conversation
src/Reflex/Vty/Widget/Layout.hs
Outdated
|
||
-- TODO should prob be (Branch [LayoutDebugTree] | Leaf PosDim | ||
-- but it's weird cuz a leaf node won't know it's PosDim until combined with a Region... | ||
data LayoutDebugTree t = LayoutDebugTree_Branch [LayoutDebugTree t] | LayoutDebugTree_Leaf |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused about what's going on with this type... t is phantom here? Is the idea to provide live debugging info about the layout tree? Maybe it would be useful to use Data.Tree for its drawTree function? I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I outlined it's intent in the attached issue but yeah basically what you said except for automated testing not debugging (pardon the name). I think it might be quite useful for non-testing cases too.
This part of the code is entirely incomplete. Once folks are OK with the overall implementation I'll move forward with finishing this part of the code.
emptyLayoutDebugTree :: LayoutDebugTree t | ||
emptyLayoutDebugTree = LayoutDebugTree_Leaf | ||
|
||
class IsLayoutReturn t b a where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This polymorphism seems unnecessary. Maybe just make a record data type something like:
data LayoutInfo t = LayoutInfo
{ layoutInfo_numChildren :: Int
, layoutInfo_focusedDyn :: Dynamic t (Maybe Int)
, layoutInfo_tree :: LayoutDebugTree t
}
and then pair it with the result of the layout thing, and a simplified version which discards this info can be provided alongside?
That seems like it should do away with the awkward ambiguous types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think you're right. I had really hoped that there wouldn't need to be multiple variations of fixed/stretch and the interface could be seamless between sublayout and non-sublayout cases. I don't think that's possible anymore in which case this type class is unnecessary 😭
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I decided to keep the typeclass because it makes the implementation of tile
simpler. The typeclass is hidden in the package. I could also just remove it and modify tile_
implementation
Thanks for working on this problem! It's something that Ali and I were thinking about how to do early on, but one of our approaches to it got mired in some details surrounding Adjustable, and we ended up leaving something oversimplified in its place. |
You're welcome! Glad to be contributing to such an awesome project! I built off the existing implementation and felt what I ended up with got a little convoluted :(. It does however work and it seems to me that the complexity is inevitable if the network needs to pass through the If the overall approach and modified interface looks OK I'll start finalizing this PR (which may take a while). |
I think it's a good start, and that we should definitely do something along these lines, even if it ends up looking different by the time it's 100% ready to go. Starting from something that works is always great. :) If you can get a simple version of this building, then it'll be easier for us to have a deeper look at it and maybe figure out how to make it more satisfying if possible. Our original idea was to have the Layout monad be the thing that kept track of tab order, so that it would usually be unnecessary to have tab order leave one runLayout and enter another and vice-versa, but if there's a reasonable scheme to make that possible, all the better. (The original way it was going to do that was with a forwards-and-backwards State monad that let us send events forward and backward to adjacent tiles in the Layout, but state monads of any kind are tricky to write satisfactory Adjustable instances for. Avoiding that shenanigans and just doing the thing in a way that involves a bit of global oversight seems the way to go.) |
The newest revision is mainly cleanup and documentation. It should compile and run now as well. For a straight-forward example, you can run Still need to implement |
Description: Monad transformer and tools for arranging widgets and building screen layouts | ||
-} | ||
{-# LANGUAGE FlexibleInstances #-} | ||
{-# LANGUAGE AllowAmbiguousTypes #-} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, what linter do y'all prefer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't been using one, but I will say that generally we don't align things beyond the margin (apart from in cases where it will make code correctness much more obvious), since it leads to people who aren't using the same tools doing a bunch of extra work to keep stuff aligned, and the subsequent whitespace changes make the diffs harder to examine.
The latest revision implements PR is feature complete. Looking forward to y'alls feedback. Related plug, I'd like to merge my testing framework reflex-vty-test-host someday since I think it's super useful for testing. It's dependent on this PR which has yet to be reviewed 😭 |
Having another look now, and I'll take a look at the testing one right after. |
@pdlla Your PR got me thinking and I'm going to improve this a bit further this weekend prior to merging. I think some of the awkwardness you're working around can be avoided entirely (though it'll involve breaking changes). |
-- | The Layout monad transformer keeps track of the configuration (e.g., 'Orientation') and | ||
-- 'Constraint's of its child widgets, apportions vty real estate to each, and acts as a | ||
-- switchboard for focus requests. See 'tile' and 'runLayout'. | ||
-- switchboard for focus requests. See 'tile_' and 'runLayout'. | ||
newtype Layout t m a = Layout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've come to think it was a mistake to combine the layout and focus selection functionality into one monad. All of this might be substantially easier if the two concepts were separate. One of the problems with the old implementation was that the NodeId order was significant - i.e., things were focused in the order they were created. We shouldn't depend on that kind of ordering at all - which I think is what the Int
you've got here is all about.
We can use ordered-containers so that we get efficient lookups while preserving order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your points. I definitely hope for a simpler implementation. Having tab navigable focus that just works is great of course but this code is rather unreasonable to understand/modify limiting its usefulness for developers.
Unclear what your proposed alternative is apart from simply removing this feature. It seems to me passing focus events into a nested layout monad tree would involve re-implementing what we have here one way or another (ordered-containers vs nodeid<->index bimap will only make things a little less complex).
This ended up growing into a larger refactor than originally intended and introduces many breaking changes (though I think they're good ones). Let me know what you think about this interface. To make that diff more consumable, I added a changelog entry, but that's still a lot of information. The executive summary is:
|
Cool, turning into a PR to make it easier for me to comment. Probably won't get through it today. |
Closing in favor of #43 |
This PR is paired with this issue explaining the changes.
Test cases for changes are here as it depends on reflex-vty-test-host which has not been merged yet.
Unfortunately the reflex frp network is a little convoluted. I think it pretty much has to be this way if we want to use the existing Layout monad design.
This PR is feature complete and has been tested.
Things left todo before PR is ready to be mergedimprove documentation, possible add diagram showing how events are processed.finish implementing and test "no focus" casesfinish implementing LayoutDebugTreewrite test cases using https://github.com/pdlla/reflex-vty-test-hostupdate example code to use new Layout stuff