Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transparency for layers/pad functions #74

Closed
sgraf812 opened this issue Aug 23, 2016 · 6 comments
Closed

Transparency for layers/pad functions #74

sgraf812 opened this issue Aug 23, 2016 · 6 comments

Comments

@sgraf812
Copy link

sgraf812 commented Aug 23, 2016

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?

@sgraf812
Copy link
Author

Transparency is allowed by the pad function of vty it seems, but I don't see where it has a counterpart in bricks API.

@sgraf812 sgraf812 changed the title Independent positioning within Widgets Transparency for layers/pad functions Aug 23, 2016
@jtdaugherty
Copy link
Owner

If I understand this right, I am hearing two requests:

  • Is there a way to do padding with transparency?
  • Is there a way to do placement in a container without knowing its size?

The answer to the first one is to take a look at translateBy, and the answer to the second is no: if you don't know how big the parent container is, you can't know how much your translation offset should be. But, it isn't too hard to do this generically. Here's a generic way to make a layer from a widget and place it at the bottom left. (I haven't tested this but it should be just about right.) The idea is to render the child first, then use its size and the parent container size (from the rendering context) to translate the child. Translation gives you the transparency you want, unlike padding. For inspiration you can look at how the "layer" variants of the centering functions are implemented (e.g. hCenterLayer).

bottomLeftCorner :: Widget a -> Widget a
bottomLeftCorner child =
  Widget Greedy Greedy $ do
    ctx <- getContext
    childResult <- render child
    let childHeight = result^.imageL.to imageHeight
        offsetX = 0
        offsetY = ctx^.availHeightL - childHeight
        offset = Location (offsetX, offsetY)
    return $ addResultOffset offset $ childResult & imageL .~ translateBy offset

@sgraf812
Copy link
Author

Thanks for the tips! I think I got what I want. I packaged it up in some margin* combinators. They seem to work, judging from fiddling around with them, but no guarantees:

marginLeft :: Padding -> Widget a -> Widget a
marginLeft padding p =
  let (pad, sz) = case padding of
        Max -> (id, Greedy)
        Pad i -> (const i, hSize p)
  in Widget sz (vSize p) $ do
      (availWidth, lim) <- calculateLim padding availWidthL
      result <- render $ hLimit lim p
      let offsetX = pad (availWidth - result^.imageL.to imageWidth)
          offset = Location (offsetX, 0)
      return $ addResultOffset offset $ result & imageL %~ translateX offsetX


marginRight :: Padding -> Widget a -> Widget a
marginRight padding p =
  let (pad, sz) = case padding of
        Max -> (id, Greedy)
        Pad i -> (const i, hSize p)
  in Widget sz (vSize p) $ do
      (availWidth, lim) <- calculateLim padding availWidthL
      result <- render $ hLimit lim p
      let childWidth = result^.imageL.to imageWidth
          width = childWidth + pad (availWidth - childWidth)
      return $ result & imageL %~ resizeWidth width


marginTop :: Padding -> Widget a -> Widget a
marginTop padding p =
  let (pad, sz) = case padding of
        Max -> (id, Greedy)
        Pad i -> (const i, vSize p)
  in Widget (hSize p) sz $ do
      (availHeight, lim) <- calculateLim padding availHeightL
      result <- render $ vLimit lim p
      let offsetY = pad (availHeight - result^.imageL.to imageHeight)
          offset = Location (0, offsetY)
      return $ addResultOffset offset $ result & imageL %~ translateY offsetY


marginBottom :: Padding -> Widget a -> Widget a
marginBottom padding p =
  let (pad, sz) = case padding of
        Max -> (id, Greedy)
        Pad i -> (const i, vSize p)
  in Widget (hSize p) sz $ do
      (availHeight, lim) <- calculateLim padding availHeightL
      result <- render $ vLimit lim p
      let childHeight = result^.imageL.to imageHeight
          height = childHeight + pad (availHeight - childHeight)
      return $ result & imageL %~ resizeHeight height


calculateLim :: Padding -> Lens' Context Int -> RenderM n (Int, Int)
calculateLim padding widthOrHeight = do
    c <- getContext
    let lim = case padding of
          Max -> c^.widthOrHeight
          Pad i -> c^.widthOrHeight - i
    return (c^.widthOrHeight, lim)

They're pretty similar to padLeft et al., feel free to use them if you want to.

@jtdaugherty jtdaugherty reopened this Aug 23, 2016
@jtdaugherty
Copy link
Owner

Looking at the implementation I'm not sure what these functions are supposed to do. Can you give a description of just the first one?

@sgraf812
Copy link
Author

sgraf812 commented Aug 23, 2016

It's more or less like the pad*, but the padding is 'transparent'. Think bottomLeftCorner = marginTop Max . marginRight Max.

So marginLeft is like padLeft, but instead of fill ' ' the padding characters, it translates over them. The resemblence should be more clear if you inline calculateLim.

marginTop is analogous, but marginBottom and marginRight have to resize the Image instead of translating it.

Is that clear enough? If not, I could make a screenshot of what I mean.

@jtdaugherty
Copy link
Owner

That makes sense. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants