jtdaugherty / brick Goto Github PK
View Code? Open in Web Editor NEWA declarative Unix terminal UI library written in Haskell
License: Other
A declarative Unix terminal UI library written in Haskell
License: Other
The new render cache has no demonstration program but one is needed.
Now that Vty has mouse support, there are some interesting possibilities for mouse support in Brick.
listReplace :: Eq e => Vector e -> Maybe Int -> List e -> List e
Is there any reason that the element of Vector must have Eq instance? (I have no idea)
I removed the Eq e
constraint and the library still compiles.
The right-padding issue I noticed on #7 was because my rendered list elements had a trailing newline. In this situation the row ends with an ellipsis, and an enclosing element's width is disrupted on that line. Eg instead of
| |
| E |
| |
you see
| |
| E…|
| |
Perhaps it should always strip trailing newlines, I expect it will be a common mistake.
The list event handler needs to support paging operations. However, to do this, the event handling code needs to know about the viewport state from the most recent rendering pass. This is a blind spot in the current design but it can be done, and luckily this is a specific case of a more general problem of event handlers needing to consider rendering state in their decisions. Here's my plan:
v <- handleEvent (st^.stuff) someEvent
continue $ st^.stuff .~ v
we'd have
continue =<< handleEventLensed st stuff someEvent
handleEventLensed :: (HandleEvent a) => s -> Lens' s a -> Event -> EventM s
handleEventLensed s func ev = do
newVal <- handleEvent (s^.func) ev
return $ s^.func .~ newVal
(CC @simonmichael)
Hi, I've been playing around with brick
a bit and I've noticed that halt
doesn't behave like mzero
. I'd like to write code that looks something like:
myHandler :: MyState -> Event -> EventM (Next MyState)
myHandler st evt = do
when (evt `isKey` KEsc) $
halt st
... further event handling ...
This is actually a somewhat larger problem for me than just when
vs. if-then-else
: I'd like to write composable, locally stateful event handlers using auto
or similar, and step them forward in the top-level event handler them. However, currently I have no way of "inspecting" the return type of a handler (Next MyState
) to see if indeed I should halt or continue.
I can easily work around this by defining my own ADT that encodes how to proceed, but it would essentially be almost identical to what already exists: either Continue
or Halt
.
Does this make sense? Thanks!
The following program crashes when the terminal width is less than max (min hlim a) (min hlim b)
, and this happens both when the initial terminal size is too small and when I manually resize terminal width after program start.
import Brick
import Brick.Widgets.Center
ui :: Widget
ui = hCenter . hLimit hlim $ vBox [s a 'A', s b 'B']
where
s x c = str . take x $ repeat c
hlim = 100
a = 60
b = 40
main :: IO ()
main = simpleMain ui
The error I got is Main.hs: row 0 now exceeds region width
.
Not sure if this is a duplicate of #47 .
Hi,
When a widget is padded, its available width and height don't seem to change. Is this intended? Presumably widgets need correct information.
Examples:
w :: Widget
w = Widget Fixed Fixed $ do
c <- getContext
render (str (show (availWidth c)))
-- shows "123"
main = simpleMain w
-- shows " 123"
main = simpleMain (padLeft (Pad 10) w)
I can reproduce a crash from within vty with the message "cannot crop height to less than zero". I don't know if this is a vty or a brick problem, but I'll report it here:
Make a list with many entries, select one that wouldn't be initially visible and then empty the list (and set the selected element to Nothing
).
Quickstart:
git clone https://github.com/rootzlevel/brick-reproduce
cd brick-reproduce
stack build --exec brick-reproduce
Then type any letter (e.g. f
) to empty the list.
Here is the code:
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Brick
import Brick.Widgets.List
import Control.Lens ((&), (.~))
import Control.Monad (void)
import qualified Data.Vector as V
import Graphics.Vty
type AppState = List String
event :: AppState -> Event -> EventM (Next AppState)
event l e = case e of
EvKey (KChar 'q') [] -> halt l
EvKey _ _ -> continue $ l & listElementsL .~ V.empty
& listSelectedL .~ Nothing
_ -> continue l
main :: IO ()
main = do
let app = App { appDraw = \l -> [renderList l (const str)]
, appChooseCursor = showFirstCursor
, appHandleEvent = event
, appAttrMap = const (attrMap defAttr [])
, appLiftVtyEvent = id
, appStartEvent = return
}
initList = list "list" (V.fromList [show x | x <- [1..500]]) 1
& listSelectedL .~ Just 400
void $ defaultMain app initList
Please bump up base bound to support GHC 7.10.
(micro)lens seems desirable for working with nested records in a brick app. I just started to exploring this, and am finding it harder than expected. Notes so far:
instance Monoid (List a)
where
mempty = list "" V.empty 1
mappend a b = a & listElementsL .~ (a^.listElementsL <> b^.listElementsL)
It's not a problem for me because they're building locally fine, but I thought you might want to know.
Example (apply this diff to EditDemo.hs):
diff --git a/programs/EditDemo.hs b/programs/EditDemo.hs
index f300f88..05ad2f0 100644
--- a/programs/EditDemo.hs
+++ b/programs/EditDemo.hs
@@ -62,6 +62,7 @@ appEvent st ev =
V.EvKey V.KEsc [] -> M.halt st
V.EvKey (V.KChar '\t') [] -> M.continue $ switchEditors st
V.EvKey V.KBackTab [] -> M.continue $ switchEditors st
+ V.EvKey (V.KChar ' ') [] -> M.continue st { _edit2 = (E.editor secondEditor (str . unlines) Nothing "hello\nworld")}
_ -> M.continue =<< T.handleEventLensed st (currentEditorL st) ev
initialState :: St
Then run it, press "TAB" (optional), then press SPACE to re-initialize/modify the 2nd editor widget to have the contents "hello\nworld". Although the words are rendered correctly (world
being on the 2nd line), the cursor thinks that it's all on one line. It looks as if the cursor is hovering over extra blank space if you press Right arrow, but that appears to be the buffer where the word world
is stored.
Tested on the latest 0.6.1 version by using cabal sandbox on GHC 7.10.3.
Could you (or me) add a list of imported names for better readability of the example programs?
On a particular screen, the items are ordered by date, so on entering it I move the selection to the last (most recent) item:
initRegisterScreen d args st@AppState{aopts=opts, ajournal=j, aScreen=s@RegisterScreen{}} =
st{aScreen=s{rsState=is'}}
where
is' =
listMoveTo (length items) $
list (Name "register") (V.fromList items) 1
items = ...
Sometimes the selected item appears at the bottom of the screen; sometimes it appears at the top (and I have to scroll up to see that there are older items). I'm not sure what controls this.
In this situation I think I'd like it to scroll down from the top just enough to reveal the selected item at the bottom of the screen.
Ideally I suppose there would be API to control List's scroll state (maybe it can expose a ViewportScroll ?)
brick causes some strage behaviour whith stdin
In this program (only little different from the hello world example) I read a String from stdin and display it.
module Main where
import Brick.Widgets.Core (str)
import Brick.Main (simpleMain)
main :: IO ()
main = do
--text <- return "Hello world"
text <- getLine
simpleMain . str $ text
No big deal, right?
Wenn I do
ghc -threaded hello.hs
./hello
> hello you
everything works nicely ("hello you" is displayed).
But for
ghc -threaded hello.hs
echo "hello you" | ./hello
which should provide the same result, an error occurs (which wasn't noticed at compiletime, this never happend to me before):
hello: getTerminalAttributes: illegal operation (Inappropriate ioctl for device)
I don't get problems when replacing the simpleMain . str
with putStrLn
, so I guess this is a problem of brick. I am running ghc 7.10.2 on an Arch Linux and tried xterm, urxvt and termite (all with the same result.
Any idea what happens here?
Hi, not sure if this behavior is intended...
{-# LANGUAGE OverloadedStrings #-}
-- hi
-- there
main = simpleMain (txt "hi\nthere")
-- hi...
main = simpleMain (markup ("hi\nthere" @@ bg blue))
Multi-line markdown appears to just put an ellipsis where the first newline should go.
The program I'm working on has a list widget, but above the list widget are a fixed number of lines of plain-old text. The presence of those header lines seems to screw up the scrolling/visibility logic a bit.
Terminal version: iTerm2 2.1.1 and Terminal 2.5.3 (both on OS X Yosemite)
GHC version: 7.10.2
brick version: 0.2
vty version: 5.4.0
A simple repro program: https://github.com/ktvoelker/brick-demo
Steps to reproduce:
Expected behavior:
4. The list scrolls and the item becomes visible.
Note that if you press Down a few more times, eventually the list does start scrolling in the right direction. But it never catches up, so the selected item is always off-screen. The size of this "offset" appears to be equal to the number of lines of header text, plus one.
I would be glad to work on fixing the bug myself, but I wanted to make sure that it actually is a bug, first.
Please consider adding brick
to stackage. The package already builds fine with GHC 8 and the stackage resolver:
$ cabal unpack brick
$ cd brick-0.8
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.0.1
$ stack init --resolver nightly --force --solver
...
$ stack build
...
Registering brick-0.8...
import Brick
import System.Console.ANSI -- ansi-terminal
main = simpleMain (str (setSGRCode [SetColor Foreground Dull Red] ++ "hi"))
For some reason, this displays ...
instead of a red hi
l = list ...
border = borderWithLabel label
hborder w = hBorderWithLabel label
<=>
w
<=>
hBorder
ui1 = border l
ui2 = hborder l
ui1 is a list inside a border; it is sized correctly. ui2 is the same list without the side borders; it overflows the bottom of the screen by one or two rows, so you can't see the bottom border or the bottom-most list row.
I've encountered a weird issue when compiling brick
demos.
I'm following the instructions in the README, and run cabal install -j -f demos
.
TL;DR :
undefined reference to symbol 'del_curterm'
/lib64/libtinfow.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
I would like behaviour where lines grow from bottom to top, for a greedy widget. lets say there is space for 10 lines. If there is one line of content, i want it to be at the bottom. if there are 20 lines of contents, i want 11-20 displayed.
I have played around with viewports/adding empty lines/translating; nothing gave this exact behaviour. closest was invoking visible
on the bottom line; but then the viewport remains translated even if the available space increases (again). (Also, this solution feels kinda hacky.)
I have been defining dummy Show instances for List, Editor etc. so that I can include them in my application types which need to be Showable for debugging, testing in GHCI etc.
instance Show (List a) where show _ = "<List>"
instance Show Editor where show _ = "<Editor>"
Hello,
I use rxvt-unicode and running the brick demos I noticed
Using brick 0.4.1 on Debian Stretch
constraints: StateVar ==1.1.0.1,
adjunctions ==4.2.2,
array ==0.5.1.0,
base ==4.8.2.0,
base-orphans ==0.4.4,
bifunctors ==5,
binary ==0.7.5.0,
blaze-builder ==0.4.0.1,
bytestring ==0.10.6.0,
comonad ==4.2.7.2,
containers ==0.5.6.2,
contravariant ==1.3.3,
data-default ==0.5.3,
data-default-class ==0.0.1,
data-default-instances-base ==0.0.1,
data-default-instances-containers ==0.0.1,
data-default-instances-dlist ==0.0.1,
data-default-instances-old-locale ==0.0.1,
deepseq ==1.4.1.1,
directory ==1.2.2.0,
distributive ==0.4.4,
dlist ==0.7.1.2,
exceptions ==0.8.0.2,
filepath ==1.4.0.0,
free ==4.12.1,
ghc-prim ==0.4.0.0,
hashable ==1.2.3.3,
integer-gmp ==1.0.0.0,
kan-extensions ==4.2.3,
lens ==4.12.3,
mtl ==2.2.1,
old-locale ==1.0.0.7,
parallel ==3.2.0.6,
parsec ==3.1.9,
prelude-extras ==0.4.0.2,
pretty ==1.1.2.0,
primitive ==0.6.1.0,
profunctors ==5.1.1,
reflection ==2,
rts ==1.0,
semigroupoids ==5.0.0.4,
semigroups ==0.16.2.2,
stm ==2.4.4,
tagged ==0.8.2,
template-haskell ==2.10.0.0,
terminfo ==0.4.0.1,
text ==1.2.1.3,
text-zipper ==0.3.1,
time ==1.5.0.1,
transformers ==0.4.2.0,
transformers-compat ==0.4.0.4,
unix ==2.7.1.0,
unordered-containers ==0.2.5.1,
utf8-string ==1.0.1.1,
vector ==0.10.12.3,
void ==0.7.1,
vty ==5.3.1
That the dialog widget is parametrised with a fixed width makes it very hard to use in a way, that would preclude crashes on terminal resize.
Most other widgets are resize-agnostic, yet the dialog isn't.
Right now, the List
rendering process utilizes the viewport
feature to simplify scrolling state tracking. While that means the list supports variable-height list items, it means we pay a (possibly big) penalty since we render all list items regardless of visibility and then crop to show the ones in view. This works fine for small lists, but the delay in rendering becomes noticeable even for realistically sized lists (thousands of elements).
This clearly isn't great.
I initially thought supporting variable-height list items as well as uniform-height items would be a slam-dunk but I'm now thinking that an optimized list is really needed in the (common) case where you know that the heights of all of your list items will be the same number of rows. The only way we can know which subset of list elements to bother rendering is by knowing how high they will be in advance, a luxury we don't have if we assume the item heights can't be precomputed. But if we restrict ourselves to the assumption that the list items all have the same known height, we can use the scrolling state and viewport size to select only the items worth rendering.
I think this is feasible, but it might increase the burden on the user slightly and might have some trade-offs that need consideration. This ticket is a reminder for me to implement this and to jot down notes about what I have in mind.
(CC @simonmichael)
Does brick require the full power of lens ? I've switched hledger-ui to microlens, which would probably speed up cabal/stack installation quite a bit, if brick did the same.
Not sure if this is a question or a feature request, but please bear with me.
From what I can tell, there is no way to assign multiple color attributes to text within a single List widget element.
What I have is basically a List of multiple strings, with each string being a particular "type", much like a spreadsheet. E.g.,
foo bar quux
foo bar quux
foo bar quux
and I want all the foo
strings to be a certain color distinct from all the bar
strings, and so on.
As I write this I realize that I could probably just have multiple List widgets, one for each "column" (one for foo
, one for bar
, etc.) and assign a different color attribute for each widget (and have them aligned horizontally, right?), but is there a way to just embed different colors into a single List widget item?
I feel that having multiple List widgets seems hacky and overly complicated.
Looking at the App
datatype, it seems to me that the interface could be expressed more concisely using some frp library such as reactive-banana
or reflex
. (And for users that otherwise already use some frp approach it needs a nasty layer between frp and the current state-passing interface.)
Has this been considered before? Do there exist any forks or other vty-reverse-deps that go in this direction?
To be precise, I'm suggesting that:
data Size = Fixed
| Greedy
be changed to
data Size = Fixed Int
| Greedy
with the semantics that Fixed n
widgets take up n
units of space, but will deal with less if they are given it, and Greedy
widgets get given whatever is left over after space is allocated to Fixed
widgets.
The benefits of the change are:
Fixed
widgets, ensures that Fixed
widgets actually have the correct semantics (this would have prevented #17, for example).boxRenderer
much more modular, because it be implemented as:
boxRenderer
uses doesn't work in 2d)boxRenderer
just crops the resulting table. It would be more sensible if it instead rendered the sub-widgets with less space than requested.The problems I can foresee are:
Fixed
widget/combinator has to be changed. This is less bad than it seems. Every widget/combinator I found in the library can be implemented easily with the obvious Monoid
and Ord
instances for Size
.Fixed
widgets directly are broken.I'm happy to start implementing this, but I want to make sure you agree with basic idea first.
I have a small demo using the following event loop
event :: State -> Event -> EventM (Next State)
event st e = do
liftIO $ print e
continue st
and the following draw function
draw :: State -> [Widget]
draw st = [str "Hello World!"]
There are several cases like Ctrl+,
, Ctrl+.
, and Ctrl+/
that do not fire an event. Additionally there is at least one case of sending the wrong event. For example you would expect Ctrl+m
to read...
EvKey (KChar 'm') [MCtrl]
however it actually reads...
EvKey KEnter []
brick-0.5 depends on unix-2.7.1.0 which failed to install
Is windows support planned?
Right now editors are showing a mysterious '...' character as well as failing to show more than the first line of text when being rendered. This might be related to recent string function changes.
Right now the editor's internal zipper holds its contents in a list of Strings. Let's use Text instead.
The top-left and top-right viewports work as advertised. The bottom one does as expected for left-right movements, but ctrl-up/down arrows affect the top-left viewport instead of the bottom one.
brick version 0.1
vty-5.2.10 (as reported in my cabal sandbox's share/x86_64-osx-ghc-7.10.2 directory)
unsure where the output of cabal freeze
goes.
The Editor widget can now be modified to support bracketed pastes from Vty more efficiently than processing input events individually.
Hi Jonathan,
thank you very much for the amazing "brick" library! It's great working with it. :)
I'm looking for a way to have layers within a "Widget", not just within an "App". I wondered if it would be possible to change the "image" in "Result" from "Image" to "[Image]", and then change "renderFinal" to flatten the "List" before passing it to "picForLayers"? Do you see any problems with that approach, and if not, would you be interested in integrating a patch if I would come up with the necessary changes?
Many thanks!
Hi,
first of all: I really like how this library feels, great job!
However, I have some problems laying out stuff for an ascii game. Consider two widgets I want to place in the top-right and bottom-left corner, respectively. I'd like that to work independent from the bounds of the containing widget. Is there a way without asking the container for its current size, e.g. reusing the pad*
functions or something? It should also be composable, e.g. if I add other widgets to the container it should not influence the positioning of the already specified widgets.
What I had in mind was similar to having a separate layer for each widget and then just pad* Max
, but unfortunately the padding characters aren't transparent.
What it boils down to, I guess, is: Is there a notion of transparency?
module Main where
import qualified Brick.Main as M
import qualified Brick.Widgets.Core as WC
ui = WC.vLimit h . WC.hLimit 5 $ WC.hBox
[ WC.str text2
, WC.vLimit h $ WC.fill '$'
]
where
h = 3
text1 = " \n寬字\n \n"
text2 = " \nabcd\n \n"
main :: IO ()
main = M.simpleMain ui
The program displays
$
abcd$
$
But when I replace WC.str text2
by WC.str text1
, it displays
寬字
Both abcd
and 寬字
occupy 4 display length in my terminal.
Maybe there is something wrong with wide character rendering?
Is there a plan to add a Tree widget?
After reading the guide and looking at the example programs, I still have no idea how I am supposed to switch from one UI to another. For instance, I want to display a sublist after choosing a specific element of the main list and display a dialog after choosing a different element of the list. Is there any way to do that? I can do something similar by using multiple Apps like so
main = do
a <- M.defaultMain listApp $ initialState ["subList1","subList2"]
case (elem a) of
Just (_,"subList1") -> M.defaultMain subList1App $ initialState [1..5]
Just (_,"subList2") -> M.defaultMain subList2App $ initialState [5..10]
Now that works, but I don't think I'm supposed to use multiple Apps. Now I could just use the same App for everything, but then I can't use different header/footer for the different Lists without introducing a state datatype. Then there's also a problem when I want to go back from a sublist to the parent list. It would be nice if there was information on how to do these things.
I’d like to make a list, like so:
124 foo
4324 bar
1 test
Padding just adds space to the side on each line, so the result becomes something like
124 foo
4324 bar
1 test
The idea is to make the left part greedy and then restrict its size.
My naive approach ((hLimit 10 $ (txt "124") { hSize = Greedy }) <+> txt "foo"
gives
124foo
…
It looks to me like it doesn’t make sense to reset hSize
without re-rendering (what hLimit
does).
How would I set a txt
or str
to greedy?
Does it even make sense to export the fields of Widget
s?
This is not necessarily a bug, I just wanted to notify you, that the brick version on stackage lts is 0.4.1, for which most examples don't work. The nightly version is up to date, however, but you might want to include a note about this in the manual.
Thanks for this library!
Haskell memory leaks still confuse me, so this behavior might be expected from my code, but I’ve noticed that when application state is carried over between events, the heap blows up very fast. E. g., for a simple app which has state based on Text
, and which appends 1k of text when each event fires, the heap size goes up very fast and memory will soon run out, even though only a linear amount of data is appended. If I change the txt
resp. str
widgets to only take as many lines and columns as available, the effect is reduced, but still present (so maybe the leak is with Vty, not Brick?).
This came up when I was trying to use a str
widget to keep a log of incoming Vty events. After a few hundred lines the address space exceeded several GB.
I can reproduce this behavior with the following gist.
The following gives the heap profile for the gist after hammering away at the keys for a second.
However, even though I should have library profiling enabled, neither the Vty or Brick modules appear in the graph, so it may well be it’s All My Fault™ after all. Maybe the issue makes more sense to you.
Thanks for making Brick!
I'm having to compare location quite often, and I'd like to avoid to define an orphan instance.
I can do the PR if you want (I believe ghc can derive Eq
correctly anyway).
Any reason it's not there ?
(disclaimer, I'm fairly new to haskell)
Hi,
Thanks for a really cool lib.
I'm interested in sorta doing full width banners or headers.
I mean full width of the viewport.
I want to do the equivalent of this
and color the background.
I know I can do this manually with spaces, but looking for a way to do it automatically to the 'edge of view' or something like that.
(Apologizes in advance, i'm new to both Brick and Haskell, but having a lot of fun so far)
Is there a need for a dropdown menu? I came across a few instances where it might come in handy and would love to give a shot at it.
This is a feature request to expose another way than handleDialogEvent
to change the state of a dialog widget. It would be nice to have support for arrows as well as tab/s-tab, and maybe something else.
So maybe directly expose (and document) nextButtonBy
or expose simplified functions like nextChoice
and prevChoice
of type Dialog -> Dialog
to handle that manually.
What do you think ?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.