vty-ui 1.7.1 → 1.8
raw patch · 37 files changed
+441/−352 lines, 37 filesdep +data-defaultdep ~vty
Dependencies added: data-default
Dependency ranges changed: vty
Files
- demos/CollectionDemo.hs +3/−2
- demos/ComplexDemo.hs +3/−2
- demos/EditDemo.hs +6/−0
- demos/ListDemo.hs +20/−22
- demos/ProgressBarDemo.hs +1/−1
- doc/ch2/event_loop.tex +1/−1
- doc/ch2/focus_groups.tex +1/−1
- doc/ch2/handling_user_input.tex +2/−2
- doc/ch3/new_widget_type.tex +5/−6
- doc/ch3/rendering.tex +1/−1
- doc/ch3/widgetimpl_api.tex +2/−2
- doc/ch4/Fixed.tex +1/−1
- doc/ch4/FormattedText.tex +4/−4
- doc/ch4/List.tex +10/−5
- doc/macros.tex +1/−1
- src/Graphics/Vty/Widgets/Borders.hs +16/−15
- src/Graphics/Vty/Widgets/Box.hs +27/−28
- src/Graphics/Vty/Widgets/Centering.hs +9/−10
- src/Graphics/Vty/Widgets/CheckBox.hs +1/−1
- src/Graphics/Vty/Widgets/Core.hs +18/−17
- src/Graphics/Vty/Widgets/DirBrowser.hs +7/−5
- src/Graphics/Vty/Widgets/Edit.hs +38/−14
- src/Graphics/Vty/Widgets/EventLoop.hs +61/−25
- src/Graphics/Vty/Widgets/Fills.hs +2/−2
- src/Graphics/Vty/Widgets/Fixed.hs +8/−8
- src/Graphics/Vty/Widgets/Group.hs +2/−2
- src/Graphics/Vty/Widgets/Limits.hs +2/−3
- src/Graphics/Vty/Widgets/List.hs +50/−38
- src/Graphics/Vty/Widgets/Padding.hs +17/−16
- src/Graphics/Vty/Widgets/ProgressBar.hs +2/−2
- src/Graphics/Vty/Widgets/Table.hs +32/−33
- src/Graphics/Vty/Widgets/Text.hs +18/−18
- src/Graphics/Vty/Widgets/Util.hs +32/−28
- test/src/Tests/FormattedText.hs +5/−4
- test/src/Tests/Instances.hs +0/−5
- test/src/Tests/Util.hs +11/−6
- vty-ui.cabal +22/−21
demos/CollectionDemo.hs view
@@ -25,7 +25,8 @@ mkSecondUI = do fg <- newFocusGroup- lst <- newTextList (green `on` blue) items 1+ lst <- newTextList items 1+ setSelectedUnfocusedAttr lst $ Just (green `on` blue) addToFocusGroup fg lst c <- centered =<< vLimit 10 =<< hLimit 50 =<< bordered lst return (c, fg)@@ -60,7 +61,7 @@ let keyHandler nextUI = \_ k mods -> case (k, mods) of- (KASCII 'n', [MCtrl]) -> nextUI >> return True+ (KChar 'n', [MCtrl]) -> nextUI >> return True (KEsc, []) -> exitSuccess _ -> return False
demos/ComplexDemo.hs view
@@ -16,7 +16,7 @@ fg = white bg = black focAttr = black `on` yellow-headerAttr = fgColor bright_green+headerAttr = fgColor brightGreen msgAttr = fgColor blue -- Multi-state checkbox value type@@ -64,7 +64,8 @@ edit1Header <- textWidget wrap T.empty >>= withNormalAttribute headerAttr edit2Header <- textWidget wrap T.empty >>= withNormalAttribute headerAttr - lst <- newList (fgColor bright_green) 1+ lst <- newList 1+ setSelectedUnfocusedAttr lst $ Just (fgColor brightGreen) selector <- vLimit 3 lst listHeader <- plainText T.empty
demos/EditDemo.hs view
@@ -10,15 +10,19 @@ e2 <- multiLineEditWidget setEditLineLimit e2 $ Just 3 e3 <- editWidget+ e4 <- editWidget+ setEditRewriter e4 (const '*') fg <- newFocusGroup _ <- addToFocusGroup fg e1 _ <- addToFocusGroup fg e2 _ <- addToFocusGroup fg e3+ _ <- addToFocusGroup fg e4 be1 <- bordered =<< boxFixed 40 5 e1 be2 <- bordered =<< boxFixed 40 3 e2 be3 <- bordered =<< boxFixed 40 1 e3+ be4 <- bordered =<< boxFixed 40 1 e4 c <- centered =<< ((plainText "Multi-Line Editor (unlimited lines):") <--> (return be1)@@ -26,6 +30,8 @@ <--> (return be2) <--> (plainText "Single-Line Editor:") <--> (return be3)+ <--> (plainText "Password input:")+ <--> (return be4) <--> (plainText "- Esc to quit\n\n- TAB to switch editors") >>= withBoxSpacing 1 )
demos/ListDemo.hs view
@@ -11,11 +11,10 @@ AppElements { theList :: Widget (List T.Text FormattedText) , theBody :: Widget FormattedText , theFooter :: Widget FormattedText- , theListLimit :: Widget (VLimit (List T.Text FormattedText)) , uis :: Collection } -titleAttr = bright_white `on` blue+titleAttr = brightWhite `on` blue focAttr = black `on` green bodyAttr = white `on` black selAttr = black `on` yellow@@ -26,19 +25,19 @@ \See the keystrokes below to try the demo." message2 :: [(T.Text, Attr)]-message2 = [ ("- Press ", def_attr), ("q", keyAttr), (" to quit\n", def_attr)- , ("- Press ", def_attr), ("+", keyAttr)- , (" / ", def_attr), ("a", keyAttr)- , (" to add a list item\n", def_attr)- , ("- Press ", def_attr), ("-", keyAttr)- , (" / ", def_attr), ("d", keyAttr)- , (" to remove the selected list item\n", def_attr)- , ("- Press ", def_attr)- , ("up", keyAttr), (" / ", def_attr)- , ("down", keyAttr), (" / ", def_attr)- , ("page up", keyAttr), (" / ", def_attr)+message2 = [ ("- Press ", defAttr), ("q", keyAttr), (" to quit\n", defAttr)+ , ("- Press ", defAttr), ("+", keyAttr)+ , (" / ", defAttr), ("a", keyAttr)+ , (" to add a list item\n", defAttr)+ , ("- Press ", defAttr), ("-", keyAttr)+ , (" / ", defAttr), ("d", keyAttr)+ , (" to remove the selected list item\n", defAttr)+ , ("- Press ", defAttr)+ , ("up", keyAttr), (" / ", defAttr)+ , ("down", keyAttr), (" / ", defAttr)+ , ("page up", keyAttr), (" / ", defAttr) , ("page down", keyAttr)- , (" to navigate the list\n", def_attr)+ , (" to navigate the list\n", defAttr) ] buildUi appst = do@@ -62,17 +61,16 @@ -- Construct the application statea using the message map. mkAppElements :: IO AppElements mkAppElements = do- lw <- newTextList selAttr [] 1+ lw <- newTextList [] 1+ setSelectedUnfocusedAttr lw $ Just selAttr b <- textWidget wrap T.empty ft <- plainText T.empty >>= withNormalAttribute titleAttr- ll <- vLimit 5 lw c <- newCollection return $ AppElements { theList = lw , theBody = b , theFooter = ft- , theListLimit = ll , uis = c } @@ -115,11 +113,11 @@ (theList st) `onKeyPressed` \_ k _ -> do case k of- (KASCII 'q') -> exitSuccess- (KASCII '-') -> removeCurrentItem >> return True- (KASCII 'd') -> removeCurrentItem >> return True- (KASCII '+') -> addNewItem >> return True- (KASCII 'a') -> addNewItem >> return True+ (KChar 'q') -> exitSuccess+ (KChar '-') -> removeCurrentItem >> return True+ (KChar 'd') -> removeCurrentItem >> return True+ (KChar '+') -> addNewItem >> return True+ (KChar 'a') -> addNewItem >> return True _ -> return False -- We need to call these handlers manually because while they will
demos/ProgressBarDemo.hs view
@@ -12,7 +12,7 @@ -- Visual attributes. focAttr = black `on` green-bodyAttr = bright_green `on` black+bodyAttr = brightGreen `on` black completeAttr = white `on` red incompleteAttr = red `on` white
doc/ch2/event_loop.tex view
@@ -137,7 +137,7 @@ \fw{someAttr `withStyle` underline} & adding a style \\ \hline \end{tabular} -The Vty \fw{def\_attr} value's default configuration is used as a+The Vty \fw{defAttr} value's default configuration is used as a basis for all partially-specified attributes. The functions described above are defined in the \fw{Util} module.
doc/ch2/focus_groups.tex view
@@ -68,7 +68,7 @@ \begin{haskellcode} fg <- newFocusGroup fg `onKeyPressed` \_ key _ ->- if key == KASCII 'q' then+ if key == KChar 'q' then exitSuccess else return False \end{haskellcode}
doc/ch2/handling_user_input.tex view
@@ -28,12 +28,12 @@ \begin{haskellcode} w `onKeyPressed` \_ key _ ->- if key == KASCII 'f' then+ if key == KChar 'f' then (launchTheMissiles >> return True) else return False w `onKeyPressed` \_ key _ ->- if key == KASCII 'q' then+ if key == KChar 'q' then exitSuccess else return False \end{haskellcode}
doc/ch3/new_widget_type.tex view
@@ -41,16 +41,15 @@ \begin{haskellcode} let st = Counter initialValue- wRef <- newWidget st $ \w -> ...+ newWidget st $ \w -> ... \end{haskellcode} The \fw{Core} module's \fw{newWidget} function creates a new \fw{IORef} wrapping a \fw{WidgetImpl a}. The \fw{WidgetImpl} type is where all of the widget logic is actually implemented. You implement this logic by overriding the fields of the \fw{WidgetImpl} type, such-as \fw{render\_}. We call \fw{newWidget}'s result \fw{wRef} because-it is a reference to a widget, and this helps distinguish it from the-actual widget data in the next step.+as \fw{render\_}. The \fw{newWidget} function will return the+\fw{IORef} as \fw{Widget Counter}; \fw{Widget} is a type alias. The \fw{newWidget} function takes an initial state of the widget (of type \fw{a}) and a transformation function \fw{WidgetImpl a ->@@ -67,7 +66,7 @@ \this size ctx -> do (Counter v) <- getState this let s = T.pack $ show v- width = (fromEnum $ region_width size) -+ width = (fromEnum $ regionWidth size) - (fromEnum $ textWidth s) (truncated, _, _) = clip1d (Phys 0) (Phys width) s return $ string (getNormalAttr ctx) $ T.unpack truncated@@ -117,7 +116,7 @@ \begin{haskellcode} let s = T.pack $ show v- width = (fromEnum $ region_width size) -+ width = (fromEnum $ regionWidth size) - (fromEnum $ textWidth s) (truncated, _, _) = clip1d (Phys 0) (Phys width) s \end{haskellcode}
doc/ch3/rendering.tex view
@@ -21,7 +21,7 @@ specified attribute. \item \fw{char} -- Creates an image from a character using the specified attribute.-\item \fw{char\_fill} -- Creates an image with the specified width and+\item \fw{charFill} -- Creates an image with the specified width and height, filled with the specified character and attribute. \item \fw{<->} -- Vertical concatenation of images. \item \fw{<|>} -- Horizontal concatenation of images.
doc/ch3/widgetimpl_api.tex view
@@ -54,10 +54,10 @@ Sometimes used when positioning child widgets and when positioning the cursor, if any. \item \fw{normalAttribute} -- the widget's normal attribute. Defaults- to Vty's \fw{def\_attr} value, which merges transparently with the+ to Vty's \fw{defAttr} value, which merges transparently with the \fw{RenderContext}'s normal attribute. \item \fw{focusAttribute} -- the widget's focus attribute. Defaults- to Vty's \fw{def\_attr} value, which merges transparently with the+ to Vty's \fw{defAttr} value, which merges transparently with the \fw{RenderContext}'s focus attribute. \item \fw{keyEventHandler} -- the action responsible for handling key events for this widget. The default implementation merely starts
doc/ch4/Fixed.tex view
@@ -51,7 +51,7 @@ guarantee that the specified space is consumed. \begin{haskellcode}- lst <- newList (green `on` black) 1+ lst <- newList 1 ui <- vFixed 5 lst \end{haskellcode}
doc/ch4/FormattedText.tex view
@@ -9,7 +9,7 @@ \fw{plainText} function and can be laid out in the usual way: \begin{haskellcode}- t1 <- plainText "blue" >>= withNormalAttribute (fgColor blue)+ t1 <- plainText "blue" >>= withNormal (fgColor blue) t2 <- plainText "green" >>= withNormalAttribute (fgColor green) ui <- (return t1) <++> (return t2) \end{haskellcode}@@ -28,7 +28,7 @@ All text widget update functions \textit{tokenize} their inputs, finding contiguous sequences of whitespace and non-whitespace characters and newlines, and store the list of tokens in the widget.-Each token is assigned a default attribute of \fw{def\_attr}, which+Each token is assigned a default attribute of \fw{defAttr}, which defaults to the ``normal'' attribute of the widget (see Section \ref{sec:attributes} for more information on attributes). @@ -48,9 +48,9 @@ \begin{haskellcode} t <- plainText "" setTextWithAttrs t [ ("foo", fgColor green)- , (" ", def_attr)+ , (" ", defAttr) , ("bar", fgColor yellow)- , (" ", def_attr)+ , (" ", defAttr) , ("baz", red `on` blue) ] \end{haskellcode}
doc/ch4/List.tex view
@@ -36,16 +36,21 @@ Lists are created with the \fw{newList} function: \begin{haskellcode}- lst <- newList attr 1+ lst <- newList 1 \end{haskellcode} -\fw{newList} takes two parameters: the attribute of the currently-selected item-to be used when the list is \textit{not} focused, and the height, in rows, of-each widget in the list. The \fw{List} uses its own focus attribute (Section+\fw{newList} takes one parameter: the height, in rows, of each widget in the+list. The \fw{List} uses its own focus attribute (Section \ref{sec:attributes}) as the attribute of the currently-selected item when it has the focus. The widget type of the list (\fw{b} above) won't be chosen by the type system until you actually add something to the list. +The \fw{List} widget uses the default focus attribute of the rendering context+to render the selected item when the list has the focus. To control how the+selected item appears when the list has or lacks focus, use see+\fw{set\-Selected\-Focused\-Attr} and+\fw{set\-Se\-lec\-ted\-Un\-fo\-cused\-Attr}.+ Items may be added to a \fw{List} with the \fw{addToList} function, which takes an internal value (e.g., \fw{String}) and a widget of the appropriate type:@@ -110,7 +115,7 @@ value, and widget of the currently-selected list item. \item \fw{getListItem} -- takes a \fw{Widget (List a b)} and an index and returns \fw{Nothing} if the \fw{List} has no item at the- specified index item or returns \fw{Just (pos, (val, widget))}+ specified index item or returns \fw{Just (val, widget)} corresponding to the list index. \end{itemize}
doc/macros.tex view
@@ -1,6 +1,6 @@ % Custom macros. -\newcommand{\vtyuiversion}{1.7.1}+\newcommand{\vtyuiversion}{1.8} \newcommand{\fw}[1]{\texttt{#1}} \newcommand{\vtyui}{\fw{vty-ui}}
src/Graphics/Vty/Widgets/Borders.hs view
@@ -71,7 +71,7 @@ -- attribute and character. hBorder :: IO (Widget HBorder) hBorder = do- let initSt = HBorder def_attr T.empty+ let initSt = HBorder defAttr T.empty wRef <- newWidget initSt $ \w -> w { growHorizontal_ = const $ return True , render_ = renderHBorder@@ -80,8 +80,8 @@ return wRef renderHBorder :: Widget HBorder -> DisplayRegion -> RenderContext -> IO Image-renderHBorder _ (DisplayRegion 0 _) _ = return empty_image-renderHBorder _ (DisplayRegion _ 0) _ = return empty_image+renderHBorder _ (0, _) _ = return emptyImage+renderHBorder _ (_, 0) _ = return emptyImage renderHBorder this s ctx = do HBorder attr str <- getState this let attr' = mergeAttrs [ overrideAttr ctx@@ -89,7 +89,7 @@ , normalAttr ctx ] ch = skinHorizontal $ skin ctx- noTitle = T.pack $ replicate (fromEnum $ region_width s) ch+ noTitle = T.pack $ replicate (fromEnum $ regionWidth s) ch wStr <- case T.null str of True -> return noTitle@@ -98,10 +98,10 @@ , str , T.pack " " ]- case (textWidth title) > (Phys $ fromEnum $ region_width s) of+ case (textWidth title) > (Phys $ fromEnum $ regionWidth s) of True -> return noTitle False -> do- let remaining = (Phys $ fromEnum $ region_width s) - (textWidth title)+ let remaining = (Phys $ fromEnum $ regionWidth s) - (textWidth title) Phys side1 = remaining `div` Phys 2 side2 = if remaining `mod` Phys 2 == Phys 0 then side1 else side1 + 1 return $ T.concat [ T.pack $ replicate side1 ch@@ -123,7 +123,7 @@ -- attribute and character. vBorder :: IO (Widget VBorder) vBorder = do- let initSt = VBorder def_attr+ let initSt = VBorder defAttr wRef <- newWidget initSt $ \w -> w { growVertical_ = const $ return True , getCursorPosition_ = const $ return Nothing@@ -133,7 +133,7 @@ , attr , normalAttr ctx ]- return $ char_fill attr' (skinVertical $ skin ctx) 1 (region_height s)+ return $ charFill attr' (skinVertical $ skin ctx) 1 (regionHeight s) } return wRef @@ -154,7 +154,7 @@ -- |Wrap a widget in a bordering box. bordered :: (Show a) => Widget a -> IO (Widget (Bordered a)) bordered child = do- let initSt = Bordered def_attr child T.empty+ let initSt = Bordered defAttr child T.empty wRef <- newWidget initSt $ \w -> w { growVertical_ = const $ growVertical child , growHorizontal_ = const $ growHorizontal child@@ -182,7 +182,7 @@ drawBordered :: (Show a) => Bordered a -> DisplayRegion -> RenderContext -> IO Image drawBordered this s ctx- | region_width s < 2 || region_height s < 2 = return empty_image+ | regionWidth s < 2 || regionHeight s < 2 = return emptyImage | otherwise = do let Bordered attr child label = this attr' = mergeAttrs [ overrideAttr ctx@@ -194,12 +194,13 @@ -- Render the contained widget with enough room to draw borders. -- Then, use the size of the rendered widget to constrain the space -- used by the (expanding) borders.- let constrained = DisplayRegion (region_width s - 2) (region_height s - 2)+ let constrained = (regionWidth s - 2, regionHeight s - 2) childImage <- render child constrained ctx - let adjusted = DisplayRegion (image_width childImage + 2)- (image_height childImage)+ let adjusted = ( imageWidth childImage + 2+ , imageHeight childImage+ ) tlCorner <- plainText (T.singleton $ skinCornerTL sk) >>= withNormalAttribute attr'@@ -225,6 +226,6 @@ leftRight <- render vb adjusted ctx - let middle = horiz_cat [leftRight, childImage, leftRight]+ let middle = horizCat [leftRight, childImage, leftRight] - return $ vert_cat [top, middle, bottom]+ return $ vertCat [top, middle, bottom]
src/Graphics/Vty/Widgets/Box.hs view
@@ -28,7 +28,6 @@ ) where -import GHC.Word ( Word ) import Data.Typeable import Control.Exception import Control.Monad@@ -81,11 +80,11 @@ -- growth comparison function , secondGrows :: IO Bool -- region dimension fetch function- , regDimension :: DisplayRegion -> Word+ , regDimension :: DisplayRegion -> Int -- image dimension fetch function- , imgDimension :: Image -> Word+ , imgDimension :: Image -> Int -- dimension modification function- , withDimension :: DisplayRegion -> Word -> DisplayRegion+ , withDimension :: DisplayRegion -> Int -> DisplayRegion -- Oriented image concatenation , img_cat :: [Image] -> Image }@@ -145,13 +144,13 @@ , secondGrows = (if o == Vertical then growVertical else growHorizontal) wb , regDimension =- if o == Vertical then region_height else region_width+ if o == Vertical then snd else fst , imgDimension =- if o == Vertical then image_height else image_width+ if o == Vertical then imageHeight else imageWidth , withDimension = if o == Vertical then withHeight else withWidth , img_cat =- if o == Vertical then vert_cat else horiz_cat+ if o == Vertical then vertCat else horizCat } wRef <- newWidget initSt $ \w ->@@ -209,9 +208,9 @@ setCurrentPosition (boxFirst b) pos case boxOrientation b of Horizontal -> setCurrentPosition (boxSecond b) $- pos `plusWidth` ((region_width ch1_size) + (toEnum $ boxSpacing b))+ pos `plusWidth` ((fst ch1_size) + (toEnum $ boxSpacing b)) Vertical -> setCurrentPosition (boxSecond b) $- pos `plusHeight` ((region_height ch1_size) + (toEnum $ boxSpacing b))+ pos `plusHeight` ((snd ch1_size) + (toEnum $ boxSpacing b)) } wRef `relayFocusEvents` wa@@ -290,34 +289,34 @@ let spAttr = getNormalAttr ctx spacing = boxSpacing this spacer = case spacing of- 0 -> empty_image+ 0 -> emptyImage _ -> case boxOrientation this of- Horizontal -> let h = max (image_height img1) (image_height img2)- in char_fill spAttr ' ' (toEnum spacing) h- Vertical -> let w = max (image_width img1) (image_width img2)- in char_fill spAttr ' ' w (toEnum spacing)+ Horizontal -> let h = max (imageHeight img1) (imageHeight img2)+ in charFill spAttr ' ' (toEnum spacing) h+ Vertical -> let w = max (imageWidth img1) (imageWidth img2)+ in charFill spAttr ' ' w (toEnum spacing) -- Use the larger of the two images to determine padding in the -- opposite dimension. E.g. if this is a vertical box, we want -- to pad the images such that they have the same width. common_opposite_dim = case boxOrientation this of- Horizontal -> max (image_height img1) (image_height img2)- Vertical -> max (image_width img1) (image_width img2)+ Horizontal -> max (imageHeight img1) (imageHeight img2)+ Vertical -> max (imageWidth img1) (imageWidth img2) padded_img1 = case boxOrientation this of Horizontal -> img1 <->- (char_fill spAttr ' ' (image_width img1)- (common_opposite_dim - image_height img1))+ (charFill spAttr ' ' (imageWidth img1)+ (common_opposite_dim - imageHeight img1)) Vertical -> img1 <|>- (char_fill spAttr ' ' (common_opposite_dim - image_width img1)- (image_height img1))+ (charFill spAttr ' ' (common_opposite_dim - imageWidth img1)+ (imageHeight img1)) padded_img2 = case boxOrientation this of Horizontal -> img2 <->- (char_fill spAttr ' ' (image_width img2)- (common_opposite_dim - image_height img2))+ (charFill spAttr ' ' (imageWidth img2)+ (common_opposite_dim - imageHeight img2)) Vertical -> img2 <|>- (char_fill spAttr ' ' (common_opposite_dim - image_width img2)- (image_height img2))+ (charFill spAttr ' ' (common_opposite_dim - imageWidth img2)+ (imageHeight img2)) return $ (img_cat this) [padded_img1, spacer, padded_img2]@@ -333,7 +332,7 @@ -- If the box is too large to fit in the available space (since it -- has fixed dimensions and can't be scaled), return the empty -- image.- | toEnum firstDim + toEnum secondDim > regDimension this s = return (empty_image, empty_image)+ | toEnum firstDim + toEnum secondDim > regDimension this s = return (emptyImage, emptyImage) | otherwise = do let withDim = withDimension this img1 <- render (boxFirst this) (s `withDim` (toEnum firstDim)) ctx@@ -341,8 +340,8 @@ -- pad the images so they fill the space appropriately. let fill img amt = case boxOrientation this of- Vertical -> char_fill (getNormalAttr ctx) ' ' (image_width img) amt- Horizontal -> char_fill (getNormalAttr ctx) ' ' amt (image_height img)+ Vertical -> charFill (getNormalAttr ctx) ' ' (imageWidth img) amt+ Horizontal -> charFill (getNormalAttr ctx) ' ' amt (imageHeight img) firstDimW = toEnum firstDim secondDimW = toEnum secondDim img1_size = (imgDimension this) img1@@ -380,7 +379,7 @@ b_img <- render b s' ctx return $ if renderDimension a_img >= regDim actualSpace- then [a_img, empty_image]+ then [a_img, emptyImage] else [a_img, b_img] renderHalves = do
src/Graphics/Vty/Widgets/Centering.hs view
@@ -11,7 +11,6 @@ ) where -import GHC.Word ( Word ) import Graphics.Vty.Widgets.Core import Graphics.Vty import Graphics.Vty.Widgets.Util@@ -34,12 +33,12 @@ img <- render child s ctx let attr' = getNormalAttr ctx- (half, half') = centered_halves region_width s (image_width img)+ (half, half') = centered_halves regionWidth s (imageWidth img) return $ if half > 0- then horiz_cat [ char_fill attr' ' ' half (image_height img)+ then horizCat [ charFill attr' ' ' half (imageHeight img) , img- , char_fill attr' ' ' half' (image_height img)+ , charFill attr' ' ' half' (imageHeight img) ] else img @@ -48,7 +47,7 @@ HCentered child <- getState this s <- getCurrentSize this chSz <- getCurrentSize child- let (half, _) = centered_halves region_width s (region_width chSz)+ let (half, _) = centered_halves regionWidth s (regionWidth chSz) chPos = pos `plusWidth` half setCurrentPosition child chPos @@ -77,12 +76,12 @@ img <- render child s ctx let attr' = getNormalAttr ctx- (half, half') = centered_halves region_height s (image_height img)+ (half, half') = centered_halves regionHeight s (imageHeight img) return $ if half > 0- then vert_cat [ char_fill attr' ' ' (image_width img) half+ then vertCat [ charFill attr' ' ' (imageWidth img) half , img- , char_fill attr' ' ' (image_width img) half'+ , charFill attr' ' ' (imageWidth img) half' ] else img @@ -91,7 +90,7 @@ VCentered child <- getState this s <- getCurrentSize this chSz <- getCurrentSize child- let (half, _) = centered_halves region_height s (region_height chSz)+ let (half, _) = centered_halves regionHeight s (regionHeight chSz) chPos = pos `plusHeight` half setCurrentPosition child chPos @@ -107,7 +106,7 @@ centered :: (Show a) => Widget a -> IO (Widget (VCentered (HCentered a))) centered wRef = vCentered =<< hCentered wRef -centered_halves :: (DisplayRegion -> Word) -> DisplayRegion -> Word -> (Word, Word)+centered_halves :: (DisplayRegion -> Int) -> DisplayRegion -> Int -> (Int, Int) centered_halves region_size s obj_sz = let remaining = region_size s - obj_sz half = remaining `div` 2
src/Graphics/Vty/Widgets/CheckBox.hs view
@@ -236,7 +236,7 @@ getCheckboxLabel = (checkboxLabel <~~) radioKeyEvent :: (Eq a) => Widget (CheckBox a) -> Key -> [Modifier] -> IO Bool-radioKeyEvent this (KASCII ' ') [] = cycleCheckbox this >> return True+radioKeyEvent this (KChar ' ') [] = cycleCheckbox this >> return True radioKeyEvent this KEnter [] = cycleCheckbox this >> return True radioKeyEvent _ _ _ = return False
src/Graphics/Vty/Widgets/Core.hs view
@@ -137,7 +137,7 @@ , overrideAttr :: Attr -- ^An override attribute to be used to override -- both the normal and focus attributes in effect- -- during rendering. Usually def_attr, this+ -- during rendering. Usually defAttr, this -- attribute is used when child widgets need to have -- their attributes overridden by a parent widget. , skin :: Skin@@ -152,7 +152,7 @@ -- |Default context settings. defaultContext :: RenderContext-defaultContext = RenderContext def_attr (white `on` blue) def_attr unicodeSkin+defaultContext = RenderContext defAttr (white `on` blue) defAttr unicodeSkin -- |The type of widget implementations, parameterized on the type of -- the widget's state.@@ -276,10 +276,10 @@ v <- visible <~ wRef case v of- False -> return empty_image+ False -> return emptyImage True -> do -- Merge the override attributes with the context. If the- -- overrides haven't been set (still def_attr), they will+ -- overrides haven't been set (still defAttr), they will -- have no effect on the context attributes. norm <- normalAttribute <~ wRef foc <- focusAttribute <~ wRef@@ -288,12 +288,13 @@ } img <- render_ impl wRef sz newCtx- let imgsz = DisplayRegion (image_width img) (image_height img)+ let imgsz = (imageWidth img, imageHeight img) - when (image_width img > region_width sz ||- image_height img > region_height sz) $ throw $ ImageTooBig (show impl) sz imgsz+ when ((imageWidth img > 0 && imageHeight img > 0) &&+ (imageWidth img > regionWidth sz || imageHeight img > regionHeight sz)) $+ throw $ ImageTooBig (show impl) sz imgsz - setCurrentSize wRef $ DisplayRegion (image_width img) (image_height img)+ setCurrentSize wRef (imageWidth img, imageHeight img) return img -- |Render a widget and set its position after rendering is complete.@@ -352,12 +353,12 @@ wRef <- newIORef $ WidgetImpl { state = initState- , render_ = \_ _ _ -> return empty_image+ , render_ = \_ _ _ -> return emptyImage , growVertical_ = const $ return False , growHorizontal_ = const $ return False , setCurrentPosition_ = \_ _ -> return ()- , currentSize = DisplayRegion 0 0- , currentPosition = DisplayRegion 0 0+ , currentSize = (0, 0)+ , currentPosition = (0, 0) , focused = False , visible = True , gainFocusHandlers = gfhs@@ -365,8 +366,8 @@ , loseFocusHandlers = lfhs , keyEventHandler = \_ _ _ -> return False , getCursorPosition_ = defaultCursorInfo- , normalAttribute = def_attr- , focusAttribute = def_attr+ , normalAttribute = defAttr+ , focusAttribute = defAttr } updateWidget wRef f@@ -375,10 +376,10 @@ -- |Default cursor positioning implementation used by 'newWidget'. defaultCursorInfo :: Widget a -> IO (Maybe DisplayRegion) defaultCursorInfo w = do- sz <- getCurrentSize w+ (rW, _) <- getCurrentSize w pos <- getCurrentPosition w- if region_width sz > 0 then- return $ Just $ pos `plusWidth` (region_width sz - 1) else+ if rW > 0 then+ return $ Just $ pos `plusWidth` (rW - 1) else return Nothing -- |Given a widget and key event information, invoke the widget's key@@ -545,7 +546,7 @@ let initSt = FocusGroup { entries = [] , currentEntryNum = -1- , nextKey = (KASCII '\t', [])+ , nextKey = (KChar '\t', []) , prevKey = (KBackTab, []) }
src/Graphics/Vty/Widgets/DirBrowser.hs view
@@ -137,7 +137,9 @@ <++> (return fileInfo) <++> (hFill ' ' 1) <++> (return errorText)) >>= withNormalAttribute (browserHeaderAttr bSkin) - l <- newList (browserUnfocusedSelAttr bSkin) 1+ l <- newList 1+ setSelectedUnfocusedAttr l $ Just (browserUnfocusedSelAttr bSkin)+ ui <- vBox header =<< vBox l footer r <- newIORef ""@@ -231,7 +233,7 @@ [ (\_ s -> isRegularFile s , \_ s -> return $ T.pack $ "regular file, " ++ (show $ fileSize s) ++ " bytes"- , def_attr)+ , defAttr) , (\_ s -> isSymbolicLink s, (\p stat -> do linkDest <- if not $ isSymbolicLink stat@@ -255,7 +257,7 @@ annotation = getAnnotation' fullPath st $ (browserCustomAnnotations sk) ++ (builtInAnnotations cur sk) - getAnnotation' _ _ [] = (def_attr, return T.empty)+ getAnnotation' _ _ [] = (defAttr, return T.empty) getAnnotation' pth stat ((f,mkAnn,a):rest) = if f pth stat then (a, mkAnn pth stat)@@ -268,8 +270,8 @@ handleBrowserKey b _ KRight [] = descend b False >> return True handleBrowserKey b _ KLeft [] = ascend b >> return True handleBrowserKey b _ KEsc [] = cancelBrowse b >> return True-handleBrowserKey b _ (KASCII 'q') [] = cancelBrowse b >> return True-handleBrowserKey b _ (KASCII 'r') [] = refreshBrowser b >> return True+handleBrowserKey b _ (KChar 'q') [] = cancelBrowse b >> return True+handleBrowserKey b _ (KChar 'r') [] = refreshBrowser b >> return True handleBrowserKey _ _ _ _ = return False -- |Refresh the browser by reloading and displaying the contents of
src/Graphics/Vty/Widgets/Edit.hs view
@@ -41,6 +41,8 @@ , getEditText , getEditCurrentLine , setEditText+ , setEditRewriter+ , setCharFilter , getEditCursorPosition , setEditCursorPosition , setEditLineLimit@@ -76,6 +78,8 @@ , cursorMoveHandlers :: Handlers (Int, Int) , lineLimit :: Maybe Int , maxLength :: Maybe Int+ , rewriter :: Char -> Char+ , charFilter :: Char -> Bool } instance Show Edit where@@ -103,6 +107,8 @@ , cursorMoveHandlers = cmhs , lineLimit = Nothing , maxLength = Nothing+ , rewriter = id+ , charFilter = const True } wRef <- newWidget initSt $ \w ->@@ -128,15 +134,28 @@ let (cursorRow, _) = Z.cursorPosition (contents st) Phys offset = physCursorCol st - clipLeft (clipRect st) newPos = pos- `withWidth` (toEnum ((fromEnum $ region_width pos) + offset))+ `withWidth` (toEnum ((fromEnum $ regionWidth pos) + offset)) `plusHeight` (toEnum (cursorRow - (fromEnum $ clipTop $ clipRect st))) return $ if f then Just newPos else Nothing +-- |Set the function which rewrites all characters at rendering time. Defaults+-- to 'id'. Does not affect text stored in the editor.+setEditRewriter :: Widget Edit -> (Char -> Char) -> IO ()+setEditRewriter w f =+ updateWidgetState w $ \st -> st { rewriter = f }++-- |Set the function which allows typed characters in the edit widget.+-- Defaults to 'const' 'True', allowing all characters. For example, setting the+-- filter to ('elem' \"0123456789\") will only allow numbers in the edit widget.+setCharFilter :: Widget Edit -> (Char -> Bool) -> IO ()+setCharFilter w f =+ updateWidgetState w $ \st -> st { charFilter = f }+ renderEditWidget :: Widget Edit -> DisplayRegion -> RenderContext -> IO Image renderEditWidget this size ctx = do- resize this ( Phys $ fromEnum $ region_height size- , Phys $ fromEnum $ region_width size )+ resizeEdit this ( Phys $ fromEnum $ regionHeight size+ , Phys $ fromEnum $ regionWidth size ) st <- getState this isFocused <- focused <~ this@@ -147,9 +166,10 @@ attr = if isFocused then focusAttr ctx else nAttr clipped = doClipping (Z.getText $ contents st) (clipRect st)+ rewritten = ((rewriter st) <$>) <$> clipped - totalAllowedLines = fromEnum $ region_height size- numEmptyLines = lim - length clipped+ totalAllowedLines = fromEnum $ regionHeight size+ numEmptyLines = lim - length rewritten where lim = case lineLimit st of Just v -> min v totalAllowedLines@@ -158,9 +178,9 @@ emptyLines = replicate numEmptyLines "" lineWidget s = let Phys physLineLength = sum $ chWidth <$> s in string attr s <|>- char_fill attr ' ' (region_width size - toEnum physLineLength) 1+ charFill attr ' ' (regionWidth size - toEnum physLineLength) 1 - return $ vert_cat $ lineWidget <$> (clipped ++ emptyLines)+ return $ vertCat $ lineWidget <$> (rewritten ++ emptyLines) doClipping :: [T.Text] -> ClipRect -> [String] doClipping ls rect =@@ -221,8 +241,8 @@ getEditMaxLength :: Widget Edit -> IO (Maybe Int) getEditMaxLength = (maxLength <~~) -resize :: Widget Edit -> (Phys, Phys) -> IO ()-resize e (newHeight, newWidth) = do+resizeEdit :: Widget Edit -> (Phys, Phys) -> IO ()+resizeEdit e (newHeight, newWidth) = do updateWidgetState e $ \st -> let newRect = (clipRect st) { clipHeight = newHeight , clipWidth = newWidth@@ -366,10 +386,10 @@ editKeyEvent this k mods = do let run f = applyEdit f this >> return True case (k, mods) of- (KASCII 'a', [MCtrl]) -> run Z.gotoBOL- (KASCII 'k', [MCtrl]) -> run Z.killToEOL- (KASCII 'e', [MCtrl]) -> run Z.gotoEOL- (KASCII 'd', [MCtrl]) -> run Z.deleteChar+ (KChar 'a', [MCtrl]) -> run Z.gotoBOL+ (KChar 'k', [MCtrl]) -> run Z.killToEOL+ (KChar 'e', [MCtrl]) -> run Z.gotoEOL+ (KChar 'd', [MCtrl]) -> run Z.deleteChar (KLeft, []) -> run Z.moveLeft (KRight, []) -> run Z.moveRight (KUp, []) -> run Z.moveUp@@ -389,7 +409,11 @@ else st return v (KDel, []) -> run Z.deleteChar- (KASCII ch, []) -> run (Z.insertChar ch)+ (KChar ch, []) -> do+ st <- getState this+ if charFilter st ch+ then run (Z.insertChar ch)+ else return True -- True even if the filter rejected the character. (KHome, []) -> run Z.gotoBOL (KEnd, []) -> run Z.gotoEOL (KEnter, []) -> do
src/Graphics/Vty/Widgets/EventLoop.hs view
@@ -10,12 +10,16 @@ , shutdownUi , newCollection , addToCollection+ , addToCollectionWithCallbacks , setCurrentEntry+ , EntryHide+ , EntryShow ) where import Data.IORef import Data.Typeable+import Data.Default (def) import Control.Concurrent ( forkIO ) import Control.Concurrent.STM ( atomically ) import Control.Concurrent.STM.TChan@@ -42,17 +46,18 @@ -- 'Collection' is empty. runUi :: Collection -> RenderContext -> IO () runUi collection ctx = do- vty <- mkVty+ vty <- mkVty def -- Create VTY event listener thread _ <- forkIO $ vtyEventListener vty eventChan + setCurrentEntry collection 0 runUi' vty eventChan collection ctx `finally` shutdown vty vtyEventListener :: Vty -> TChan CombinedEvent -> IO () vtyEventListener vty chan = forever $ do- e <- next_event vty+ e <- nextEvent vty atomically $ writeTChan chan $ VTYEvent e -- |Schedule a widget-mutating 'IO' action to be run by the main event@@ -68,20 +73,20 @@ runUi' :: Vty -> TChan CombinedEvent -> Collection -> RenderContext -> IO () runUi' vty chan collection ctx = do- sz <- display_bounds $ terminal vty+ sz <- displayBounds $ outputIface vty e <- getCurrentEntry collection let fg = entryFocusGroup e - img <- entryRenderAndPosition e (DisplayRegion 0 0) sz ctx- update vty $ pic_for_image img+ img <- entryRenderAndPosition e (0, 0) sz ctx+ update vty $ picForLayers [img] mPos <- getCursorPosition fg case mPos of- Just (DisplayRegion w h) -> do- show_cursor $ terminal vty- set_cursor_pos (terminal vty) w h- Nothing -> hide_cursor $ terminal vty+ Just (w, h) -> do+ showCursor $ outputIface vty+ setCursorPos (outputIface vty) w h+ Nothing -> hideCursor $ outputIface vty -- Get the next event(s) in the queue. Returns all available events; -- blocks until at least one event is available.@@ -114,8 +119,16 @@ instance Exception CollectionError -data Entry = forall a. (Show a) => Entry (Widget a) (Widget FocusGroup)+type EntryShow = IO ()+type EntryHide = IO () +data Entry = forall a. (Show a) => Entry+ { entryWidget :: Widget a+ , entryFocusGroup :: Widget FocusGroup+ , entryShowCallback :: EntryShow+ , entryHideCallback :: EntryHide+ }+ data CollectionData = CollectionData { entries :: [Entry] , currentEntryNum :: Int@@ -132,10 +145,7 @@ ] entryRenderAndPosition :: Entry -> DisplayRegion -> DisplayRegion -> RenderContext -> IO Image-entryRenderAndPosition (Entry w _) = renderAndPosition w--entryFocusGroup :: Entry -> Widget FocusGroup-entryFocusGroup (Entry _ fg) = fg+entryRenderAndPosition (Entry { entryWidget = w }) = renderAndPosition w -- |Create a new collection. newCollection :: IO Collection@@ -144,24 +154,41 @@ , currentEntryNum = -1 } +getMaybeCurrentEntry :: Collection -> IO (Maybe Entry)+getMaybeCurrentEntry cRef = do+ cur <- currentEntryNum <~ cRef+ es <- entries <~ cRef+ if cur == -1+ then return Nothing+ else if cur >= 0 && cur < length es+ then return . Just $ es !! cur+ else return Nothing+ getCurrentEntry :: Collection -> IO Entry getCurrentEntry cRef = do+ maybeEntry <- getMaybeCurrentEntry cRef cur <- currentEntryNum <~ cRef- es <- entries <~ cRef- if cur == -1 then- throw $ BadCollectionIndex cur else- if cur >= 0 && cur < length es then- return $ es !! cur else- throw $ BadCollectionIndex cur+ case maybeEntry of+ Nothing -> throw $ BadCollectionIndex cur+ Just entry -> return entry -- |Add a widget and its focus group to a collection. Returns an -- action which, when invoked, will switch to the interface specified -- in the call. addToCollection :: (Show a) => Collection -> Widget a -> Widget FocusGroup -> IO (IO ())-addToCollection cRef wRef fg = do+addToCollection cRef wRef fg = addToCollectionWithCallbacks cRef wRef fg (return ()) (return ())++-- |Add a widget and its focus group to a collection. In addition, two+-- callbacks -- one to call when showing the widget and one to call when hiding+-- it (i.e. showing some other widget) -- must be provided. Returns an action+-- which, when invoked, will switch to the interface specified.+addToCollectionWithCallbacks :: (Show a) => Collection -> Widget a+ -> Widget FocusGroup -> EntryShow+ -> EntryHide -> IO (IO ())+addToCollectionWithCallbacks cRef wRef fg onShowCb onHideCb = do i <- (length . entries) <~ cRef modifyIORef cRef $ \st ->- st { entries = (entries st) ++ [Entry wRef fg]+ st { entries = (entries st) ++ [Entry wRef fg onShowCb onHideCb] , currentEntryNum = if currentEntryNum st == -1 then 0 else currentEntryNum st@@ -172,9 +199,18 @@ setCurrentEntry :: Collection -> Int -> IO () setCurrentEntry cRef i = do st <- readIORef cRef- if i < length (entries st) && i >= 0 then- (modifyIORef cRef $ \s -> s { currentEntryNum = i }) else- throw $ BadCollectionIndex i + if i < length (entries st) && i >= 0+ then do+ -- Let the old entry know it's no longer current.+ maybeOldEntry <- getMaybeCurrentEntry cRef+ case maybeOldEntry of+ Nothing -> return ()+ Just oldEntry -> entryHideCallback oldEntry+ -- Set the current entry index.+ (modifyIORef cRef $ \s -> s { currentEntryNum = i })+ else throw $ BadCollectionIndex i+ e <- getCurrentEntry cRef+ entryShowCallback e resetFocusGroup $ entryFocusGroup e
src/Graphics/Vty/Widgets/Fills.hs view
@@ -28,7 +28,7 @@ let attr' = mergeAttrs [ if foc then focusAttr ctx else overrideAttr ctx , normalAttr ctx ]- return $ char_fill attr' ch (region_width s) (region_height s)+ return $ charFill attr' ch (regionWidth s) (regionHeight s) , getCursorPosition_ = const $ return Nothing }@@ -50,7 +50,7 @@ let attr' = mergeAttrs [ if foc then focusAttr ctx else overrideAttr ctx , normalAttr ctx ]- return $ char_fill attr' ch (region_width s) (toEnum height)+ return $ charFill attr' ch (regionWidth s) (toEnum height) , getCursorPosition_ = const $ return Nothing }
src/Graphics/Vty/Widgets/Fixed.hs view
@@ -35,12 +35,12 @@ wRef <- newWidget initSt $ \w -> w { render_ = \this s ctx -> do HFixed width ch <- getState this- let region = s `withWidth` fromIntegral (min (toEnum width) (region_width s))+ let region = s `withWidth` fromIntegral (min (toEnum width) (regionWidth s)) img <- render ch region ctx -- Pad the image if it's smaller than the region.- let img' = if image_width img < region_width region- then img <|> (char_fill (getNormalAttr ctx) ' '- (toEnum width - image_width img)+ let img' = if imageWidth img < regionWidth region+ then img <|> (charFill (getNormalAttr ctx) ' '+ (toEnum width - imageWidth img) 1) else img return img'@@ -72,13 +72,13 @@ , render_ = \this s ctx -> do VFixed height ch <- getState this- let region = s `withHeight` fromIntegral (min (toEnum height) (region_height s))+ let region = s `withHeight` fromIntegral (min (toEnum height) (regionHeight s)) img <- render ch region ctx -- Pad the image if it's smaller than the region.- let img' = if image_height img < region_height region- then img <-> (char_fill (getNormalAttr ctx) ' '+ let img' = if imageHeight img < regionHeight region+ then img <-> (charFill (getNormalAttr ctx) ' ' 1- (toEnum height - image_height img))+ (toEnum height - imageHeight img)) else img return img'
src/Graphics/Vty/Widgets/Group.hs view
@@ -13,7 +13,7 @@ where import Control.Monad ( when )-import Graphics.Vty ( empty_image )+import Graphics.Vty ( emptyImage ) import Graphics.Vty.Widgets.Core -- |A group of widgets of a specified type.@@ -65,7 +65,7 @@ , render_ = \this sz ctx -> do cur <- currentEntryNum <~~ this case cur of- (-1) -> return empty_image+ (-1) -> return emptyImage _ -> do e <- currentEntry this render e sz ctx
src/Graphics/Vty/Widgets/Limits.hs view
@@ -21,7 +21,6 @@ where import Control.Monad-import Graphics.Vty import Graphics.Vty.Widgets.Core import Graphics.Vty.Widgets.Util @@ -39,7 +38,7 @@ , growVertical_ = const $ growVertical child , render_ = \this s ctx -> do HLimit width ch <- getState this- let region = s `withWidth` fromIntegral (min (toEnum width) (region_width s))+ let region = s `withWidth` fromIntegral (min (toEnum width) (regionWidth s)) render ch region ctx , setCurrentPosition_ =@@ -70,7 +69,7 @@ , render_ = \this s ctx -> do VLimit height ch <- getState this- let region = s `withHeight` fromIntegral (min (toEnum height) (region_height s))+ let region = s `withHeight` fromIntegral (min (toEnum height) (regionHeight s)) render ch region ctx , setCurrentPosition_ =
src/Graphics/Vty/Widgets/List.hs view
@@ -19,6 +19,8 @@ , addToList , insertIntoList , removeFromList+ , setSelectedFocusedAttr+ , setSelectedUnfocusedAttr -- ** List manipulation , scrollBy , scrollUp@@ -92,7 +94,8 @@ -- /value type/ @a@, the type of internal values used to refer to the -- visible representations of the list contents, and the /widget type/ -- @b@, the type of widgets used to represent the list visually.-data List a b = List { selectedUnfocusedAttr :: !Attr+data List a b = List { selectedUnfocusedAttr :: Maybe Attr+ , selectedFocusedAttr :: Maybe Attr , selectedIndex :: !Int -- ^The currently selected list index. , scrollTopIndex :: !Int@@ -112,6 +115,7 @@ instance Show (List a b) where show lst = concat [ "List { " , "selectedUnfocusedAttr = ", show $ selectedUnfocusedAttr lst+ , ", selectedFocusedAttr = ", show $ selectedFocusedAttr lst , ", selectedIndex = ", show $ selectedIndex lst , ", scrollTopIndex = ", show $ scrollTopIndex lst , ", scrollWindowSize = ", show $ scrollWindowSize lst@@ -120,16 +124,16 @@ , " }" ] -newListData :: Attr -- ^The attribute of the selected item- -> Int -- ^Item widget height in rows+newListData :: Int -- ^Item widget height in rows -> IO (List a b)-newListData selAttr h = do+newListData h = do schs <- newHandlers iahs <- newHandlers irhs <- newHandlers iacths <- newHandlers - return $ List { selectedUnfocusedAttr = selAttr+ return $ List { selectedUnfocusedAttr = Nothing+ , selectedFocusedAttr = Nothing , selectedIndex = -1 , scrollTopIndex = 0 , scrollWindowSize = 0@@ -213,6 +217,18 @@ -- Return the removed item. return (label, w) +-- |Sets the attributes to be merged on the selected list item when the list+-- widget has the focus.+setSelectedFocusedAttr :: Widget (List a b) -> Maybe Attr -> IO ()+setSelectedFocusedAttr w attr = do+ updateWidgetState w $ \l -> l { selectedFocusedAttr = attr }++-- |Sets the attributes to be merged on the selected list item when the list+-- widget does not have the focus.+setSelectedUnfocusedAttr :: Widget (List a b) -> Maybe Attr -> IO ()+setSelectedUnfocusedAttr w attr = do+ updateWidgetState w $ \l -> l { selectedUnfocusedAttr = attr }+ -- |Add an item to the list. Its widget will be constructed from the -- specified internal value using the widget constructor passed to -- 'newList'.@@ -301,14 +317,13 @@ , listItems = V.empty } --- |Create a new list using the specified attribute for the--- currently-selected element when the list does NOT have focus.+-- |Create a new list. The list's item widgets will be rendered using the+-- specified height in rows. newList :: (Show b) =>- Attr -- ^The attribute of the selected item- -> Int -- ^Height of list item widgets in rows+ Int -- ^Height of list item widgets in rows -> IO (Widget (List a b))-newList selAttr ht = do- list <- newListData selAttr ht+newList ht = do+ list <- newListData ht wRef <- newWidget list $ \w -> w { keyEventHandler = listKeyEvent @@ -331,12 +346,9 @@ -- Resize the list based on the available space and the -- height of each item. when (h > 0) $- resize this (max 1 ((fromEnum $ region_height sz) `div` h))-- listData <- getState this- foc <- focused <~ this+ resizeList this (max 1 ((fromEnum $ regionHeight sz) `div` h)) - renderListWidget foc listData sz ctx+ renderListWidget this sz ctx , setCurrentPosition_ = \this pos -> do@@ -374,55 +386,55 @@ Nothing -> return False Just (_, (_, e)) -> handleKeyEvent e k mods -renderListWidget :: (Show b) => Bool -> List a b -> DisplayRegion -> RenderContext -> IO Image-renderListWidget foc list s ctx = do+renderListWidget :: (Show b) => Widget (List a b) -> DisplayRegion -> RenderContext -> IO Image+renderListWidget this s ctx = do+ list <- getState this+ foc <- focused <~ this+ let items = map (\((_, w), sel) -> (w, sel)) $ getVisibleItems_ list+ childSelFocAttr = (maybe (focusAttr ctx) id) $ selectedFocusedAttr list+ childSelUnfocAttr = (maybe (focusAttr ctx) id) $ selectedUnfocusedAttr list defaultAttr = mergeAttrs [ overrideAttr ctx , normalAttr ctx ] renderVisible [] = return [] renderVisible ((w, sel):ws) = do- na <- normalAttribute <~ w let att = if sel then if foc- then focusAttr ctx- else mergeAttrs [ selectedUnfocusedAttr list- , defaultAttr- ]- else mergeAttrs [ na- , defaultAttr- ]+ then mergeAttrs [ childSelFocAttr, defaultAttr ]+ else mergeAttrs [ childSelUnfocAttr, defaultAttr ]+ else defaultAttr+ -- Height-limit the widget by wrapping it with a VFixed/VLimit limited <- vLimit (itemHeight list) =<< vFixed (itemHeight list) w img <- render limited s $ ctx { overrideAttr = att } - let actualHeight = min (region_height s) (toEnum $ itemHeight list)- img' = img <|> char_fill att ' '- (region_width s - image_width img)+ let actualHeight = min (regionHeight s) (toEnum $ itemHeight list)+ img' = img <|> charFill att ' '+ (regionWidth s - imageWidth img) actualHeight imgs <- renderVisible ws return (img':imgs) - let filler = char_fill defaultAttr ' ' (region_width s) fill_height+ let filler = charFill defaultAttr ' ' (regionWidth s) fill_height fill_height = if scrollWindowSize list == 0- then region_height s+ then regionHeight s else toEnum $ ((scrollWindowSize list - length items) * itemHeight list) visible_imgs <- renderVisible items - return $ vert_cat (visible_imgs ++ [filler])+ return $ vertCat (visible_imgs ++ [filler]) -- |A convenience function to create a new list using 'Text' values as -- the internal values and 'FormattedText' widgets to represent those -- strings.-newTextList :: Attr -- ^The attribute of the selected item- -> [T.Text] -- ^The list items+newTextList :: [T.Text] -- ^The list items -> Int -- ^Maximum number of rows of text to show per list item -> IO (Widget (List T.Text FormattedText))-newTextList selAttr labels h = do- list <- newList selAttr h+newTextList labels h = do+ list <- newList h forM_ labels $ \l -> (addToList list l =<< plainText l) return list@@ -480,8 +492,8 @@ where matcher = \(match, _) -> item == match -resize :: Widget (List a b) -> Int -> IO ()-resize wRef newSize = do+resizeList :: Widget (List a b) -> Int -> IO ()+resizeList wRef newSize = do when (newSize == 0) $ throw ResizeError size <- (scrollWindowSize . state) <~ wRef
src/Graphics/Vty/Widgets/Padding.hs view
@@ -19,7 +19,6 @@ ) where -import Data.Word import Data.Monoid import Graphics.Vty import Graphics.Vty.Widgets.Core@@ -53,16 +52,16 @@ instance Paddable Padding where pad p1 p2 = p1 +++ p2 -leftPadding :: Padding -> Word+leftPadding :: Padding -> Int leftPadding (Padding _ _ _ l) = toEnum l -rightPadding :: Padding -> Word+rightPadding :: Padding -> Int rightPadding (Padding _ r _ _) = toEnum r -bottomPadding :: Padding -> Word+bottomPadding :: Padding -> Int bottomPadding (Padding _ _ b _) = toEnum b -topPadding :: Padding -> Word+topPadding :: Padding -> Int topPadding (Padding t _ _ _) = toEnum t -- |Padding constructor with no padding.@@ -111,19 +110,21 @@ , growHorizontal_ = const $ growHorizontal ch , render_ =- \this sz ctx ->- if (region_width sz < 2) || (region_height sz < 2)- then return empty_image- else do- Padded child p <- getState this+ \this sz ctx -> do+ Padded child p <- getState this++ if (regionWidth sz < leftPadding p + rightPadding p) ||+ (regionHeight sz < bottomPadding p + topPadding p) then+ return emptyImage else+ do f <- focused <~ this -- Compute constrained space based on padding -- settings. let constrained = sz `withWidth` (toEnum $ max 0 newWidth) `withHeight` (toEnum $ max 0 newHeight)- newWidth = (fromEnum $ region_width sz) - fromEnum (leftPadding p + rightPadding p)- newHeight = (fromEnum $ region_height sz) - fromEnum (topPadding p + bottomPadding p)+ newWidth = (fromEnum $ regionWidth sz) - fromEnum (leftPadding p + rightPadding p)+ newHeight = (fromEnum $ regionHeight sz) - fromEnum (topPadding p + bottomPadding p) attr = mergeAttrs [ if f then focusAttr ctx else overrideAttr ctx , normalAttr ctx ]@@ -132,11 +133,11 @@ img <- render child constrained ctx -- Create padding images.- let leftImg = char_fill attr ' ' (leftPadding p) (image_height img)- rightImg = char_fill attr ' ' (rightPadding p) (image_height img)- topImg = char_fill attr ' ' (image_width img + leftPadding p + rightPadding p)+ let leftImg = charFill attr ' ' (leftPadding p) (imageHeight img)+ rightImg = charFill attr ' ' (rightPadding p) (imageHeight img)+ topImg = charFill attr ' ' (imageWidth img + leftPadding p + rightPadding p) (topPadding p)- bottomImg = char_fill attr ' ' (image_width img + leftPadding p + rightPadding p)+ bottomImg = charFill attr ' ' (imageWidth img + leftPadding p + rightPadding p) (bottomPadding p) return $ topImg <-> (leftImg <|> img <|> rightImg) <-> bottomImg
src/Graphics/Vty/Widgets/ProgressBar.hs view
@@ -67,9 +67,9 @@ complete_width = Phys $ fromEnum $ (toRational prog / toRational (100.0 :: Double)) *- (toRational $ fromEnum $ region_width size)+ (toRational $ fromEnum $ regionWidth size) - full_width = Phys $ fromEnum $ region_width size+ full_width = Phys $ fromEnum $ regionWidth size full_str = truncateText full_width $ mkStr txt al mkStr s AlignLeft =
src/Graphics/Vty/Widgets/Table.hs view
@@ -28,7 +28,6 @@ import Data.Monoid import qualified Data.Text as T import Data.Typeable-import Data.Word import Data.List import Control.Applicative hiding ((<|>)) import Control.Exception@@ -216,7 +215,7 @@ , columnSpecs = specs , borderStyle = borderSty , numColumns = length specs- , borderAttr = def_attr+ , borderAttr = defAttr , defaultCellAlignment = AlignLeft , defaultCellPadding = padNone }@@ -238,21 +237,21 @@ sideBorderL <- mkSideBorder this ctx True sideBorderR <- mkSideBorder this ctx False - let body = vert_cat $ intersperse rowBorder rowImgs- withTBBorders = vert_cat [topBorder, body, bottomBorder]- withSideBorders = horiz_cat [sideBorderL, withTBBorders, sideBorderR]+ let body = vertCat $ intersperse rowBorder rowImgs+ withTBBorders = vertCat [topBorder, body, bottomBorder]+ withSideBorders = horizCat [sideBorderL, withTBBorders, sideBorderR] -- Ideally, we would only display rows that we have room -- to render, but this is a much easier cop-out. :)- if ((region_width sz < image_width withSideBorders) ||- (region_height sz < image_height withSideBorders)) then- return empty_image else+ if ((regionWidth sz < imageWidth withSideBorders) ||+ (regionHeight sz < imageHeight withSideBorders)) then+ return emptyImage else return withSideBorders , setCurrentPosition_ = \this pos -> do sz <- getCurrentSize this- if region_width sz == 0 || region_height sz == 0 then+ if regionWidth sz == 0 || regionHeight sz == 0 then return () else do bs <- borderStyle <~~ this@@ -272,7 +271,7 @@ cellPhysSizes <- forM row $ \cell -> case cell of TableCell cw _ _ -> getCurrentSize cw- EmptyCell -> return $ DisplayRegion 0 1+ EmptyCell -> return (0, 1) -- Include 1 as a possible height to -- prevent zero-height images from@@ -280,7 +279,7 @@ -- won't hurt in the case where other -- cells are bigger, since their heights -- will be chosen instead.- let maxSize = maximum $ 1 : map region_height cellPhysSizes+ let maxSize = maximum $ 1 : map regionHeight cellPhysSizes borderOffset = if rowBorders bs then 1 else 0 @@ -289,7 +288,7 @@ positionRow this bs rowPos row positionRows (height + maxSize + borderOffset) rest - positionRows (region_height pos + edgeOffset) rs+ positionRows (regionHeight pos + edgeOffset) rs } return t @@ -322,7 +321,7 @@ bs <- borderStyle <~~ t if not $ rowBorders bs then- return empty_image else+ return emptyImage else mkRowBorder_ t sz ctx intChar -- Make a row border that matches the width of each row but does not@@ -343,13 +342,13 @@ intersection = string bAttr' [intChar] imgs = (flip map) szs $ \s -> case s of- ColFixed n -> char_fill bAttr' (skinHorizontal sk) n 1- ColAuto -> char_fill bAttr' (skinHorizontal sk) aw 1+ ColFixed n -> charFill bAttr' (skinHorizontal sk) n 1+ ColAuto -> charFill bAttr' (skinHorizontal sk) aw 1 imgs' = if colBorders bs then intersperse intersection imgs else imgs - return $ horiz_cat imgs'+ return $ horizCat imgs' mkTopBottomBorder :: Widget Table -> DisplayRegion -> RenderContext -> Char -> IO Image mkTopBottomBorder t sz ctx intChar = do@@ -357,7 +356,7 @@ if edgeBorders bs then mkRowBorder_ t sz ctx intChar else- return empty_image+ return emptyImage -- Make vertical side borders for the table, including row border -- intersections if necessary.@@ -367,7 +366,7 @@ if edgeBorders bs then mkSideBorder_ t ctx isLeft else- return empty_image+ return emptyImage mkSideBorder_ :: Widget Table -> RenderContext -> Bool -> IO Image mkSideBorder_ t ctx isLeft = do@@ -396,16 +395,16 @@ rowHeights <- forM rs $ \(TableRow row) -> do hs <- forM row $ \cell -> case cell of- TableCell cw _ _ -> region_height <$> getCurrentSize cw+ TableCell cw _ _ -> regionHeight <$> getCurrentSize cw EmptyCell -> return 1 return $ maximum hs - let borderImgs = (flip map) rowHeights $ \h -> char_fill bAttr' (skinVertical sk) 1 h+ let borderImgs = (flip map) rowHeights $ \h -> charFill bAttr' (skinVertical sk) 1 h withIntersections = if rowBorders bs then intersperse intersection borderImgs else borderImgs - return $ vert_cat $ topCorner : withIntersections ++ [bottomCorner]+ return $ vertCat $ topCorner : withIntersections ++ [bottomCorner] positionRow :: Widget Table -> BorderStyle -> DisplayRegion -> [TableCell] -> IO () positionRow t bs pos cells = do@@ -434,7 +433,7 @@ doPositioning 0 $ zip szs cells -autoWidth :: Widget Table -> DisplayRegion -> IO Word+autoWidth :: Widget Table -> DisplayRegion -> IO Int autoWidth t sz = do specs <- columnSpecs <~~ t bs <- borderStyle <~~ t@@ -448,7 +447,7 @@ edgeWidth = if edgeBorders bs then 2 else 0 colWidth = if colBorders bs then (toEnum $ length sizes - 1) else 0 - return $ toEnum ((max 0 ((fromEnum $ region_width sz) - totalFixed - edgeWidth - colWidth))+ return $ toEnum ((max 0 ((fromEnum $ regionWidth sz) - totalFixed - edgeWidth - colWidth)) `div` numAuto) -- |Add a heading row to a table. Adds a row using the specified@@ -543,8 +542,8 @@ rowBorders BorderFull = True rowBorders _ = False -rowHeight :: [Image] -> Word-rowHeight = maximum . map image_height+rowHeight :: [Image] -> Int+rowHeight = maximum . map imageHeight renderRow :: Widget Table -> DisplayRegion -> [TableCell] -> RenderContext -> IO Image renderRow tbl sz cells ctx = do@@ -563,7 +562,7 @@ cellImgs <- forM (zip cells sizes) $ \(cellW, sizeSpec) -> do- let cellSz = DisplayRegion cellWidth (region_height sz)+ let cellSz = (cellWidth, regionHeight sz) cellWidth = case sizeSpec of ColFixed n -> toEnum n ColAuto -> aw@@ -571,16 +570,16 @@ img <- renderCell cellSz cellW $ ctx { normalAttr = newDefault } -- Right-pad the image if it isn't big enough to fill the -- cell.- case compare (image_width img) (region_width cellSz) of+ case compare (imageWidth img) (regionWidth cellSz) of EQ -> return img- LT -> return $ img <|> char_fill att ' '- (max 0 (region_width cellSz - image_width img))- (max (image_height img) 1)+ LT -> return $ img <|> charFill att ' '+ (max 0 (regionWidth cellSz - imageWidth img))+ (max (imageHeight img) 1) GT -> throw CellImageTooBig let maxHeight = rowHeight cellImgs cellImgsBottomPadded = (flip map) cellImgs $ \img ->- img <-> char_fill att ' ' (image_width img) (maxHeight - image_height img)+ img <-> charFill att ' ' (imageWidth img) (maxHeight - imageHeight img) -- If we need to draw borders in between columns, do that. let bAttr' = mergeAttrs [ overrideAttr ctx@@ -589,6 +588,6 @@ ] withBorders = case colBorders borderSty of False -> cellImgsBottomPadded- True -> intersperse (char_fill bAttr' (skinVertical sk) 1 maxHeight) cellImgsBottomPadded+ True -> intersperse (charFill bAttr' (skinVertical sk) 1 maxHeight) cellImgsBottomPadded - return $ horiz_cat withBorders+ return $ horizCat withBorders
src/Graphics/Vty/Widgets/Text.hs view
@@ -57,7 +57,7 @@ -- |The type of formatted text widget state. Stores the text itself -- and the formatter used to apply attributes to the text. data FormattedText =- FormattedText { text :: TextStream Attr+ FormattedText { textContent :: TextStream Attr , formatter :: !Formatter , useFocusAttribute :: !Bool }@@ -90,7 +90,7 @@ wrap :: Formatter wrap = Formatter $ \sz ts -> do- let width = Phys $ fromEnum $ region_width sz+ let width = Phys $ fromEnum $ fst sz return $ wrapStream width ts -- |Construct a text widget formatted with the specified formatters@@ -98,7 +98,7 @@ -- given here (and, depending on the formatter, order might matter). textWidget :: Formatter -> T.Text -> IO (Widget FormattedText) textWidget format s = do- let initSt = FormattedText { text = TS []+ let initSt = FormattedText { textContent = TS [] , formatter = format , useFocusAttribute = False }@@ -110,7 +110,7 @@ ft <- getState this f <- focused <~ this appearFocused <- useFocusAttribute <~~ this- renderText (text ft) (f && appearFocused) (formatter ft) size ctx+ renderText (textContent ft) (f && appearFocused) (formatter ft) size ctx } setText wRef s return wRef@@ -135,17 +135,17 @@ -- |Set the text value of a 'FormattedText' widget. The specified -- string will be 'tokenize'd. setText :: Widget FormattedText -> T.Text -> IO ()-setText wRef s = setTextWithAttrs wRef [(s, def_attr)]+setText wRef s = setTextWithAttrs wRef [(s, defAttr)] -- |Append the text value to the text contained in a 'FormattedText' widget. -- The specified string will be 'tokenize'd. appendText :: Widget FormattedText -> T.Text -> IO ()-appendText wRef s = appendTextWithAttrs wRef [(s, def_attr)]+appendText wRef s = appendTextWithAttrs wRef [(s, defAttr)] -- |Prepend the text value to the text contained in a 'FormattedText' widget. -- The specified string will be 'tokenize'd. prependText :: Widget FormattedText -> T.Text -> IO ()-prependText wRef s = prependTextWithAttrs wRef [(s, def_attr)]+prependText wRef s = prependTextWithAttrs wRef [(s, defAttr)] -- |Prepend text to the text value of a 'FormattedText' widget directly, in -- case you have done formatting elsewhere and already have text with@@ -155,7 +155,7 @@ prependTextWithAttrs :: Widget FormattedText -> [(T.Text, Attr)] -> IO () prependTextWithAttrs wRef pairs = _setTextWithAttrs wRef pairs f where- f st new = let TS old = text st+ f st new = let TS old = textContent st in new ++ old -- |Append text to the text value of a 'FormattedText' widget directly, in case@@ -166,7 +166,7 @@ appendTextWithAttrs :: Widget FormattedText -> [(T.Text, Attr)] -> IO () appendTextWithAttrs wRef pairs = _setTextWithAttrs wRef pairs f where- f st new = let TS old = text st+ f st new = let TS old = textContent st in old ++ new -- |Set the text value of a 'FormattedText' widget directly, in case@@ -186,7 +186,7 @@ ts = concat $ map streamEntities streams updateWidgetState wRef $ \st ->- st { text = TS $ f st ts }+ st { textContent = TS $ f st ts } -- |Low-level text-rendering routine. renderText :: TextStream Attr@@ -211,13 +211,13 @@ lineImgs = map mkLineImg ls lineLength l = length $ tokenStr <$> l maxLineLength = maximum $ lineLength <$> ls- emptyLineLength = min (fromEnum $ region_width sz) maxLineLength+ emptyLineLength = min (fromEnum $ fst sz) maxLineLength ls = map truncLine $ map (map entityToken) $ findLines newText- truncLine = truncateLine (Phys $ fromEnum $ region_width sz)+ truncLine = truncateLine (Phys $ fromEnum $ fst sz) -- When building images for empty lines, it's critical that we- -- *don't* use the empty_image, because that will cause empty+ -- *don't* use the emptyImage, because that will cause empty -- lines to collapse vertically. Instead, we create line images -- using spaces. When doing this it's important to make them as -- wide as the rest of the text because otherwise we could have@@ -225,15 +225,15 @@ -- affecting the whole empty line. We compute the length of the -- longest line above and use that to build the 'empty' lines. mkLineImg line = if null line- then char_fill attr' ' ' emptyLineLength 1- else horiz_cat $ map mkTokenImg line- nullImg = string def_attr ""+ then charFill attr' ' ' emptyLineLength 1+ else horizCat $ map mkTokenImg line+ nullImg = string defAttr "" mkTokenImg :: Token Attr -> Image mkTokenImg tok = string (finalAttr tok) (T.unpack $ tokenStr tok) - return $ if region_height sz == 0+ return $ if snd sz == 0 then nullImg else if null ls || all null ls then nullImg- else vert_cat $ take (fromEnum $ region_height sz) lineImgs+ else vertCat $ take (fromEnum $ snd sz) lineImgs
src/Graphics/Vty/Widgets/Util.hs view
@@ -18,17 +18,15 @@ , chWidth , strWidth , textWidth+ , regionWidth+ , regionHeight , Phys(..) ) where import Control.Applicative-import Data.Word import qualified Data.Text as T import Graphics.Vty-import Graphics.Vty.Image- ( safe_wcwidth- ) -- A newtype to wrap physical screen coordinates, as opposed to -- character-logical coordinates. Used when transforming cursor@@ -38,7 +36,7 @@ deriving (Num, Eq, Show, Ord, Integral, Enum, Real) chWidth :: Char -> Phys-chWidth = Phys . fromEnum . safe_wcwidth+chWidth = Phys . fromEnum . safeWcwidth textWidth :: T.Text -> Phys textWidth = strWidth . T.unpack@@ -61,21 +59,21 @@ -- |Infix attribute constructor. Use: foregroundColor `on` -- backgroundColor. on :: Color -> Color -> Attr-on a b = def_attr `with_back_color` b `with_fore_color` a+on a b = defAttr `withBackColor` b `withForeColor` a -- |Foreground-only attribute constructor. Background color and style -- are defaulted. fgColor :: Color -> Attr-fgColor = (def_attr `with_fore_color`)+fgColor = (defAttr `withForeColor`) -- |Background-only attribute constructor. Foreground color and style -- are defaulted. bgColor :: Color -> Attr-bgColor = (def_attr `with_back_color`)+bgColor = (defAttr `withBackColor`) -- |Style-only attribute constructor. Colors are defaulted. style :: Style -> Attr-style = (def_attr `with_style`)+style = (defAttr `withStyle`) -- Left-most attribute's fields take precedence. -- |Merge two attributes. Leftmost attribute takes precedence where@@ -84,42 +82,42 @@ -- style mask will take precedence if any are set. mergeAttr :: Attr -> Attr -> Attr mergeAttr a b =- let b1 = case attr_style a of- SetTo v -> b { attr_style = SetTo v }+ let b1 = case attrStyle a of+ SetTo v -> b { attrStyle = SetTo v } _ -> b- b2 = case attr_fore_color a of- SetTo v -> b1 `with_fore_color` v+ b2 = case attrForeColor a of+ SetTo v -> b1 `withForeColor` v _ -> b1- b3 = case attr_back_color a of- SetTo v -> b2 `with_back_color` v+ b3 = case attrBackColor a of+ SetTo v -> b2 `withBackColor` v _ -> b2 in b3 -- |List fold version of 'mergeAttr'. mergeAttrs :: [Attr] -> Attr-mergeAttrs attrs = foldr mergeAttr def_attr attrs+mergeAttrs attrs = foldr mergeAttr defAttr attrs -- |Modify the width component of a 'DisplayRegion'.-withWidth :: DisplayRegion -> Word -> DisplayRegion-withWidth (DisplayRegion _ h) w = DisplayRegion w h+withWidth :: DisplayRegion -> Int -> DisplayRegion+withWidth (_, h) w = (w, h) -- |Modify the height component of a 'DisplayRegion'.-withHeight :: DisplayRegion -> Word -> DisplayRegion-withHeight (DisplayRegion w _) h = DisplayRegion w h+withHeight :: DisplayRegion -> Int -> DisplayRegion+withHeight (w, _) h = (w, h) -- |Modify the width component of a 'DisplayRegion'.-plusWidth :: DisplayRegion -> Word -> DisplayRegion-plusWidth (DisplayRegion w' h) w =- if (fromEnum w' + fromEnum w < 0)+plusWidth :: DisplayRegion -> Int -> DisplayRegion+plusWidth (w', h) w =+ if (w' + w < 0) then error $ "plusWidth: would overflow on " ++ (show w') ++ " + " ++ (show w)- else DisplayRegion (w + w') h+ else ((w + w'), h) -- |Modify the height component of a 'DisplayRegion'.-plusHeight :: DisplayRegion -> Word -> DisplayRegion-plusHeight (DisplayRegion w h') h =- if (fromEnum h' + fromEnum h < 0)+plusHeight :: DisplayRegion -> Int -> DisplayRegion+plusHeight (w, h') h =+ if (h' + h < 0) then error $ "plusHeight: would overflow on " ++ (show h') ++ " + " ++ (show h)- else DisplayRegion w (h + h')+ else (w, (h + h')) remove :: Int -> [a] -> [a] remove pos as = (take pos as) ++ (drop (pos + 1) as)@@ -130,3 +128,9 @@ repl :: Int -> a -> [a] -> [a] repl !pos !a !as = inject pos a (remove pos as)++regionWidth :: DisplayRegion -> Int+regionWidth = fst++regionHeight :: DisplayRegion -> Int+regionHeight = snd
test/src/Tests/FormattedText.hs view
@@ -9,22 +9,23 @@ import Graphics.Vty import Graphics.Vty.Widgets.Text import Graphics.Vty.Widgets.Core+import Graphics.Vty.Widgets.Util import Tests.Util import Tests.Instances () sz :: DisplayRegion-sz = DisplayRegion 100 100+sz = (100, 100) textHeight :: Property textHeight = monadicIO $ forAllM textString $ \str -> do w <- run $ plainText str img <- run $ render w sz defaultContext- if region_height sz == 0 then- return $ image_height img == 0 else- return $ image_height img == (toEnum $ numNewlines str + 1)+ if regionHeight sz == 0 then+ return $ imageHeight img == 0 else+ return $ imageHeight img == (toEnum $ numNewlines str + 1) textImageSize :: Property textImageSize =
test/src/Tests/Instances.hs view
@@ -22,11 +22,6 @@ instance Arbitrary Attr where arbitrary = Attr <$> arbitrary <*> arbitrary <*> arbitrary -instance Arbitrary DisplayRegion where- arbitrary = DisplayRegion <$> coord <*> coord- where- coord = sized $ \n -> fromIntegral <$> choose (0, n)- instance Random Phys where random g = let (val, g') = random g
test/src/Tests/Util.hs view
@@ -12,7 +12,7 @@ imageSize :: Image -> DisplayRegion -> Bool imageSize img sz =- image_width img <= region_width sz && image_height img <= region_height sz+ imageWidth img <= regionWidth sz && imageHeight img <= regionHeight sz count :: (a -> Bool) -> [a] -> Int count _ [] = 0@@ -21,15 +21,20 @@ numNewlines :: T.Text -> Int numNewlines = count (== '\n') . T.unpack +sizeGen :: Gen DisplayRegion+sizeGen = (,)+ <$> (arbitrary `suchThat` (>= 0))+ <*> (arbitrary `suchThat` (>= 0))+ sizeTest :: (Show a) => IO (Widget a) -> PropertyM IO Bool sizeTest mkWidget =- forAllM arbitrary $ \sz -> do+ forAllM sizeGen $ \sz -> do w <- run mkWidget img <- run $ render w sz defaultContext- if region_height sz == 0 || region_width sz == 0 then- return $ image_height img == 0 && image_width img == 0 else- return $ image_width img <= region_width sz &&- image_height img <= region_height sz+ if regionHeight sz == 0 || regionWidth sz == 0 then+ return $ imageHeight img == 0 && imageWidth img == 0 else+ return $ imageWidth img <= regionWidth sz &&+ imageHeight img <= regionHeight sz lineLength :: [Token a] -> Phys lineLength = sum . (textWidth <$>) . (tokenStr <$>)
vty-ui.cabal view
@@ -1,5 +1,5 @@ Name: vty-ui-Version: 1.7.1+Version: 1.8 Synopsis: An interactive terminal user interface library for Vty @@ -89,7 +89,7 @@ Library Build-Depends: base >= 4 && < 5,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, containers >= 0.2 && < 0.6, regex-base >= 0.93 && < 0.94, directory >= 1.0 && < 1.3,@@ -99,7 +99,8 @@ stm >= 2.1 && < 2.5, array >= 0.3.0.0 && < 0.6.0.0, text >= 0.11,- vector >= 0.9+ vector >= 0.9,+ data-default >= 0.5.3 GHC-Options: -Wall @@ -139,11 +140,11 @@ QuickCheck >= 2.4 && < 2.8, random >= 1.0, text,- vty < 5,+ vty >= 5.1.0, vty-ui CPP-Options: -DTESTING- GHC-Options: -Wall+ GHC-Options: -Wall -threaded if flag(no-tests) Buildable: False@@ -162,12 +163,12 @@ Executable vty-ui-collection-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: CollectionDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, text, vty-ui if !flag(demos)@@ -175,12 +176,12 @@ Executable vty-ui-list-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: ListDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, text, vty-ui if !flag(demos)@@ -188,12 +189,12 @@ Executable vty-ui-progressbar-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: ProgressBarDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, text, vty-ui if !flag(demos)@@ -201,7 +202,7 @@ Executable vty-ui-complex-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: ComplexDemo.hs Build-Depends: base >= 4 && < 5,@@ -209,7 +210,7 @@ bytestring >= 0.9 && < 1.0, time >= 1.1 && < 1.5, old-locale >= 1.0 && < 1.1,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, text, vty-ui if !flag(demos)@@ -217,24 +218,24 @@ Executable vty-ui-dirbrowser-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: DirBrowserDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, vty-ui if !flag(demos) Buildable: False Executable vty-ui-phoneinput-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: PhoneInputDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, text, vty-ui if !flag(demos)@@ -242,12 +243,12 @@ Executable vty-ui-dialog-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: DialogDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, text, vty-ui if !flag(demos)@@ -255,12 +256,12 @@ Executable vty-ui-edit-demo Hs-Source-Dirs: demos- GHC-Options: -Wall+ GHC-Options: -Wall -threaded Main-is: EditDemo.hs Build-Depends: base >= 4 && < 5, mtl >= 2.0 && < 2.3,- vty >= 4.7.0.18 && < 5,+ vty >= 5.1.0, vty-ui if !flag(demos) Buildable: False