Skip to content

Commit

Permalink
Support tui list item selection via mouse click
Browse files Browse the repository at this point in the history
  • Loading branch information
dfordivam committed Nov 23, 2024
1 parent 8b6063f commit bf76272
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 8 deletions.
10 changes: 9 additions & 1 deletion lib-tui/GHCup/Brick/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,19 @@ app :: AttrMap -> AttrMap -> App BrickState () Name
app attrs dimAttrs =
App { appDraw = drawUI dimAttrs
, appHandleEvent = eventHandler
, appStartEvent = return ()
, appStartEvent = setupVtyMode
, appAttrMap = const attrs
, appChooseCursor = Brick.showFirstCursor
}

-- | Enable mouse mode if supported by the terminal
setupVtyMode :: EventM Name BrickState ()
setupVtyMode = do
vty <- Brick.getVtyHandle
let output = Vty.outputIface vty
when (Vty.supportsMode output Vty.Mouse) $
liftIO $ Vty.setMode output Vty.Mouse True

drawUI :: AttrMap -> BrickState -> [Widget Name]
drawUI dimAttrs st =
let
Expand Down
1 change: 1 addition & 0 deletions lib-tui/GHCup/Brick/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ pattern HadrianGhcSelectBox = ResourceId 22
-- to have all of them defined, just in case
data Name = AllTools -- ^ The main list widget
| Singular Tool -- ^ The particular list for each tool
| ListItem Tool Int -- ^ An item in list
| KeyInfoBox -- ^ The text box widget with action informacion
| TutorialBox -- ^ The tutorial widget
| ContextBox -- ^ The resource for Context Menu
Expand Down
13 changes: 9 additions & 4 deletions lib-tui/GHCup/Brick/Widgets/Navigation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ draw dimAttrs section_list
minTagSize = V.maximum $ V.map (length . intercalate "," . fmap tagToString . lTag) allElements
minVerSize = V.maximum $ V.map (\ListResult{..} -> T.length $ tVerToText (GHCTargetVersion lCross lVer)) allElements
in Brick.withDefAttr L.listAttr $ SectionList.renderSectionList (renderItem minTagSize minVerSize) True bis
renderItem minTagSize minVerSize b listResult@ListResult{lTag = lTag', ..} =
renderItem minTagSize minVerSize listIx b listResult@ListResult{lTag = lTag', ..} =
let marks = if
| lSet -> (Brick.withAttr Attributes.setAttr $ Brick.str Common.setSign)
| lInstalled -> (Brick.withAttr Attributes.installedAttr $ Brick.str Common.installedSign)
Expand All @@ -100,8 +100,8 @@ draw dimAttrs section_list
| elem Latest lTag' && not lInstalled =
Brick.withAttr Attributes.hoorayAttr
| otherwise = id
active = if b then Common.enableScreenReader Common.AllTools else id
in hooray $ active $ dim
active = if b then Common.enableScreenReader (Common.ListItem lTool listIx) else id
in Brick.clickable (Common.ListItem lTool listIx) $ hooray $ active $ dim
( marks
<+> Brick.padLeft (Pad 2)
( minHSize 6
Expand Down Expand Up @@ -146,4 +146,9 @@ draw dimAttrs section_list
Nothing -> mempty
Just d -> [Brick.withAttr Attributes.dayAttr $ Brick.str (show d)])

minHSize s' = Brick.hLimit s' . Brick.vLimit 1 . (<+> Brick.fill ' ')
minHSize s' = Brick.hLimit s' . Brick.vLimit 1 . (<+> Brick.fill ' ')

instance SectionList.ListItemSectionNameIndex Common.Name where
getListItemSectionNameIndex = \case
Common.ListItem tool ix -> Just (Common.Singular tool, ix)
_ -> Nothing
19 changes: 16 additions & 3 deletions lib-tui/GHCup/Brick/Widgets/SectionList.hs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ makeLensesFor [("sectionListFocusRing", "sectionListFocusRingL"), ("sectionListE

type SectionList n e = GenericSectionList n V.Vector e

-- | To support selection by mouse click we need to obtain section name and item
-- index from the name of the item that got clicked. This helper class is to get that
class ListItemSectionNameIndex n where
getListItemSectionNameIndex :: n -> Maybe (n, Int)

-- | Build a SectionList from nonempty list. If empty we could not defined sectionL lenses.
sectionList :: Foldable t
Expand Down Expand Up @@ -129,6 +133,13 @@ moveUp = do
Just new_l -> Common.zoom (sectionL new_l) (Brick.modify L.listMoveToEnd)
else Common.zoom (sectionL l) $ Brick.modify L.listMoveUp

sectionListSelectItem :: (L.Splittable t, Eq n, ListItemSectionNameIndex n, Foldable t) => n -> EventM n (GenericSectionList n t e) ()
sectionListSelectItem selectedItem = case getListItemSectionNameIndex selectedItem of
Nothing -> pure ()
Just (secName, ix) -> do
sectionListFocusRingL %= F.focusSetCurrent secName
Common.zoom (sectionL secName) (Brick.modify $ L.listMoveTo ix)

-- | Handle events for list cursor movement. Events handled are:
--
-- * Up (up arrow key). If first element of section, then jump prev section
Expand All @@ -137,12 +148,14 @@ moveUp = do
-- * Page Down (PgDown)
-- * Go to next section (Tab)
-- * Go to prev section (BackTab)
handleGenericListEvent :: (Foldable t, L.Splittable t, Ord n)
-- * Select an element via Mouse left click
handleGenericListEvent :: (Foldable t, L.Splittable t, Ord n, ListItemSectionNameIndex n)
=> BrickEvent n a
-> EventM n (GenericSectionList n t e) ()
handleGenericListEvent (VtyEvent (Vty.EvResize _ _)) = pure ()
handleGenericListEvent (VtyEvent (Vty.EvKey (Vty.KChar '\t') [])) = sectionListFocusRingL %= F.focusNext
handleGenericListEvent (VtyEvent (Vty.EvKey Vty.KBackTab [])) = sectionListFocusRingL %= F.focusPrev
handleGenericListEvent (MouseDown n Vty.BLeft _ _) = sectionListSelectItem n
handleGenericListEvent (MouseDown _ Vty.BScrollDown _ _) = moveDown
handleGenericListEvent (MouseDown _ Vty.BScrollUp _ _) = moveUp
handleGenericListEvent (VtyEvent (Vty.EvKey Vty.KDown [])) = moveDown
Expand All @@ -156,7 +169,7 @@ handleGenericListEvent _ = pure ()

-- This re-uses Brick.Widget.List.renderList
renderSectionList :: forall n t e . (Traversable t, Ord n, Show n, Eq n, L.Splittable t, Semigroup (t e))
=> (Bool -> e -> Widget n) -- ^ Rendering function of the list element, True for the selected element
=> (Int -> Bool -> e -> Widget n) -- ^ Rendering function of the list element, True for the selected element
-> Bool -- ^ Whether the section list has focus
-> GenericSectionList n t e -- ^ The section list to render
-> Widget n
Expand All @@ -177,7 +190,7 @@ renderSectionList renderElem sectionFocus ge@(GenericSectionList focus elms slNa
sectionIsFocused l = sectionFocus && (Just (L.listName l) == F.focusGetCurrent focus)

renderInnerList :: Bool -> L.GenericList n t e -> Widget n
renderInnerList hasFocus l = Brick.vLimit (length l) $ L.renderList (\b -> renderElem (b && hasFocus)) hasFocus l
renderInnerList hasFocus l = Brick.vLimit (length l) $ L.renderListWithIndex (\i b -> renderElem i (b && hasFocus)) hasFocus l

-- compute the location to focus on within the active section
(c, r) :: (Int, Int) = case sectionListSelectedElement ge of
Expand Down

0 comments on commit bf76272

Please sign in to comment.