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

Layout does not support tab focus shifting on nested layouts #37

Closed
minimapletinytools opened this issue Jan 24, 2021 · 1 comment
Closed
Assignees

Comments

@minimapletinytools
Copy link
Member

minimapletinytools commented Jan 24, 2021

TLDR; see draft PR

Problem Outline

Tab navigation in the current Layout system does not work when Layouts are nested

For example:

col $ do
  -- row 1
  fixed 5 $ row $ do
    fixed 1 $ someWidget1
    fixed 1 $ someWidget2
    fixed 1 $ someWidget3
  -- row 2
  fixed 5 $ row $ do
    fixed 1 $ someWidget4
    fixed 1 $ someWidget5
    fixed 1 $ someWidget6

assume row1 + someWidget1 are in focus

The desired behavior is:

  • pressing "tab" will focus row1 and someWidget2
  • pressing "tab" will switch focus from someWidget<n> to someWidget<n+1 mod 6> and switch focus between row1 and row2 as necessary

However, instead we have:

  • pressing "tab" will focus row2 and someWidget2
  • in addition, pressing tab again will focus row1 and someWidget2

Challenges

Broadly speaking, Layout alternates between Layout and VtyWidget and we need to pass data across this boundary in order for tabbing to work as desired.

Using the existing Layout module as a starting point, there are 2 major challenges to support the desired behavior outlined above:

  1. runLayout needs to be aware of nested layout nodes in order to focus the correct tile
  2. tile needs to be aware of when a particular nested node index should be focus

Solution

  1. is addressed by introducing the new class below. NOTE, LayoutTree is discussed later.
class IsLayoutReturn t b a where
  getLayoutResult :: b -> a
  getLayoutNumChildren :: b -> Int
  getLayoutFocussedDyn :: b -> Dynamic t (Maybe Int)
  getLayoutTree :: b -> LayoutTree t

instance IsLayoutReturn t (LayouTree t, Dynamic t (Maybe Int), Int, a) a where
  getLayoutResult (_,_,_,a) = a
  getLayoutNumChildren (_,_,d,_) = d
  getLayoutFocussedDyn (_,d,_,_) = d
  getLayoutTree (tree,_,_,_) = tree

instance Reflex t => IsLayoutReturn t a a where
  getLayoutResult = id
  getLayoutNumChildren _ = 1
  getLayoutFocussedDyn _ = constDyn Nothing
  getLayoutTree _ = emptyLayoutTree
  1. is address by wrapping the VtyWidget monad with additional contextual information
class IsLayoutVtyWidget l t (m :: * -> *) where
  runIsLayoutVtyWidget :: l t m a -> Event t (Maybe Int) -> VtyWidget t m a

newtype LayoutVtyWidget t m a = LayoutVtyWidget {
    unLayoutVtyWidget :: ReaderT (Event t (Maybe Int)) (VtyWidget t m) a
  } deriving (...)

instance MonadTrans (LayoutVtyWidget t) where
  lift x = LayoutVtyWidget $ lift $ lift x

instance IsLayoutVtyWidget VtyWidget t m where
  runIsLayoutVtyWidget w _ = w

instance IsLayoutVtyWidget LayoutVtyWidget t m where
  runIsLayoutVtyWidget = runReaderT . unLayoutVtyWidget

Note that since we have instance LayoutReturn t a a in 1. and instance IsLayoutVtyWidget VtyWidget t m in 2. they work with both layout and non-layout nodes.

Interface Changes

Since focus events now come from the top level layout node, a new method is needed:

beginLayout :: LayoutVtyWidget -> VtyWidget
beginLayout child = do
  tab <- tabNavigation
  runLayoutVtyWidget child tab

Unfortunately, the interface is not seamless in the case of 1. as the desired monadic return value type is ambiguous as we have both

IsLayoutReturn t (LayoutTree t, Dynamic t (Maybe Int), Int, a) a
IsLayoutReturn t (LayoutTree t, Dynamic t (Maybe Int), Int, a) (LayoutTree t, Dynamic t (Maybe Int), Int, a)

So we introduce new variants fixedD and stretchD that make the type deductions above explicit.

Testing

LayoutTree is an optional returned object of type Dynamic t (Tree Region) objects of all the children. The intent is to use for automated testing (e.g. get me the absolute coordinates of (5,7) in relative coordinates of the third node of the second node of some LayoutTree). You can see an example of this here. I imagine it might be useful in non-testing scenarios as well.

Code

I've opened up the following draft PR that implements most of the above for discussion.

@ali-abrar
Copy link
Member

Fixed in #43

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

4 participants