Feature request: support percentages for width and height #412

Closed
tjvantoll opened this Issue Jul 8, 2015 · 44 comments

Projects

None yet
@tjvantoll
Contributor

My use case is wanting layout items to take up a certain portion of the screen across screen resolutions. For example suppose I'm building this UI:

I want to say that the <TextBox> and <Button>s are width: 80% (or that their parent <StackLayout>) is, but currently I have to pick an arbitrary numeric amount.

I can get the behavior I seek if I create elements in JS, by getting the screen width and using that to derive percentage-based widths, but that's a lot of work. I'd love to be able to type %, in CSS as well as layouts—especially <GridLayout>.

@bsatrom
bsatrom commented Jul 8, 2015

+1.

After all, life is about percentages. There are few absolutes...

@alejonext

+1 a grid

@atanasovg
Member

@tjvantoll This is a good feedback and I think there shouldn't be any technical challenges to implement it. @hshristov What do you think?

Otherwise you may achieve the scenario (or at least a close version) by using different approach - you may have the horizontal/vertical alignments set to stretch and use the margin property to define some spacing from the screen edges.

@emiloberg
Contributor

As I wrote in the CSS properties issue this is an important part to create good ui. Big 👍

@hshristov
Member

@tjvantoll Wouldn't margin do the same thing (as @atanasovg) suggested? Also all Layout subclasses have padding property which allows you to move all children instead of adding margin to all of them.

As for the % width/height I will have to spike it to see what type of problems will arise. It should takes percent of the width/height its parent gives it when measured, right? So it is not really single calculation but instead depends on the available size.

@tjvantoll
Contributor

Hey @hshristov,

The problem with margin is that I still have to hardcode a numeric value, which doesn't let me get accurate proportions across devices. In the login form above say I want that <TextField> to have a width of 80%. On a 320-“pixel”-wide device I can set Margin: 32 and be all set. But if I open that same code on my iPad the <TextField> is going to take up a lot more than 80% of the screen.

It should takes percent of the width/height its parent gives it when measured, right? So it is not really single calculation but instead depends on the available size.

Correct. If the parent has a width 320, and I add TextField { width: 80%; }, I'd expect the <TextField> to have a width of 256.

@valentinstoychev
Contributor

Hey - @tjvantoll - I know you are web ninja - how would you do this in web page? Would bootstrap or flexbox help you?

@tjvantoll
Contributor

For this layout I'd toss width: 80% on a container element, and make everything within the container width: 100%. For example: http://jsbin.com/qihoqanogu/1/edit?html,css,output.

Flexbox tends to be a bit clunky for really simple scenarios, but invaluable for more advanced layouts—especially in a responsive design context, as it lets you change the layout based on screen size elegantly.

Bootstrap's grid system, or Foundation's, or one of the many other that exist, are very commonly used for layout in the web world. I'm personally not a fan as I find the abstraction just adds complexity; I'd rather hand code the CSS.

@bfattori

+1

The only issue is that the element with the absolute size shouldn't be too far up the visual display tree. I feel like it might slow down the layout process to a crawl if everything were relative via percentages. So, rather than trying to mimic the browser where it crawls up the DOM tree looking for the first positionable parent, instead require that the parent has a fixed size. Or, if that's not reasonable, implement the sizing code natively.

I'd hate to see the mess that is static vs relative/absolute when one of the parents doesn't have a defined size and it has to skip around trying to find one until it lands on the body element.

@valentinstoychev
Contributor

Thanks @bfattori and @tjvantoll . We will incorporate this feedback into the research with the spike. The performance notes are definitely very important and we will make sure to measure the overhead.

@emiloberg
Contributor

Is there an update on this issue? I totally agree with the 'S:High' label.

@hshristov
Member

Hi all,

I've spiked simple % based layout and I have few cases in which I don't know what is the expected result.
For example in this scenario:

<StackLayout orientation="vertica">
   <Button text="button 1" horizontalAlignment="left" width="50%" height="50%" />
   <Button text="button 2" horizontalAlignment="stretch" width="50%" height="50%" />
   <Button text="button 3" />
</StackLayout>

So in this case questions are:

  • Should first button get half of the available size, second - 1/4, third - the remaining? Or should first two buttons both equal of the available height and no space for third one?
  • Should first button width be 50% from the available width no matter that its alignment is left or should it work only if alignment is stretch (like the second button)
  • More generally when width is set the element is measured/layouted with this width. So should widthPercent be treated the same way? If so there won't be height for the third button.

Edit:
I've checked Android support library and they have chosen the option where width\height percents are based on the parent size and they apply these values on ViewGroup children (before children are measured) so it appears like children have explicit width/height set. This is easier to implement but I'm not sure it will cover all scenarios. In their case the third button won't be shown too.

Please share your feedback.

@emiloberg
Contributor

My 2 cents:

  • Should first button get half of the available size, second - 1/4, third - the remaining? Or should first two buttons both equal of the available height and no space for third one?

I'd treat this as faulty code. Button 1 and 2 should be 50% leaving no space for Button 3. However

   <Button text="button 1" horizontalAlignment="left" width="10%" height="50%" />
   <Button text="button 2" horizontalAlignment="stretch" width="20%" height="50%" />
   <Button text="button 3" />

should leave 70 % for button 3.

  • Should first button width be 50% from the available width no matter that its alignment is left or should it work only if alignment is stretch (like the second button)

A width declaration should take precedence

  • More generally when width is set the element is measured/layouted with this width. So should widthPercent be treated the same way? If so there won't be height for the third button.

Not really following this question.

@hshristov
Member

@emiloberg
should leave 70 % for button 3.
You mean that it should leave 70% of the width which is given to the StackLayout and stackLayout is horizontal, right?

Not really following this question.
I was wondering should widthPercent actually change width of the element before it is measured so that it gets exactly the size based on parent measure size or should it be based on the remaining space like in the above example first button will get - 1/2 of the available height, then second one will get 1/4.

@jlooper
jlooper commented Sep 8, 2015

I would have hoped for a way to layout a grid with percentages, something like:

<GridLayout columns="50%,50%" rows="auto, *" >

and then we can have the items within the grid columns either stretch, have a percentage width within its parent, or have an absolute value. Is this the direction you're going? Maybe this is just a sneaky request for Flexbox ...

@hshristov
Member

I saw the missing GridLayout :) (nice github feature to drop everything in < >)
Well actually I was thinking about percent support only for width, height, marginLeft, marginRight, marginTop and marginBottom. No other properties but I guess if this is needed I can research it.
btw:

<GridLayout columns="*, *" />

will give you the same as columns="50%,50%". Is there something I'm missing?
As for the Flexbox I'm searching for use-cases :)

@jlooper
jlooper commented Sep 8, 2015

@hhristov bad example on my part. What about when you have to have uneven widths, like 20%,60%,20%?

@hshristov
Member

@jlooper - same as ** columns="1_, 3_, 1_" *_.
When there is * (star) column the grid takes all the available width that remains after fixed and auto columns are measured. If there are more star columns the space is distributed based on the star count. That means that 3* column will get three times the width of 1* column.

What bugs me in the flexbox layout is that it is supposed to be used without changing the html. I'm by no means html, css expert - quite the opposite so please correct me if I'm wrong. You just mark one element as flexbox container and that's it. But in the native platforms (like Android and WU) elements cannot be added everywhere. Only ViewGroup (in Android) can have children.

So it will not be possible to specify the layout using only CSS. One should have something like FlexboxLayout element somewhere in the XML.

Doesn't this defeat the purpose of flexbox?

@emiloberg
Contributor

You mean that it should leave 70% of the width which is given to the StackLayout and stackLayout is horizontal, right?

Right

I was wondering should widthPercent actually change width of the element before it is measured so that it gets exactly the size based on parent measure size or should it be based on the remaining space like in the above example first button will get - 1/2 of the available height, then second one will get 1/4

I'm unsure if I'm answering the question but:
Imho: both height and width should change the size of the element based on the parent measured size. Eg. an element with width 100% should take the full width of the parent element.

Example:

<StackLayout orientation="horizontal">
    <StackLayout orientation="horizontal" width="80%">
       <Button text="button 1" width="50%" />
       <Button text="button 2" width="50%" />
    </StackLayout>
    <StackLayout orientation="horizontal" width="20%">
       <Button text="button 3" width="100%" />
    </StackLayout>
</StackLayout>

Button 1 here takes 40 % of the screen width, so does button 2. Whereas button 3 takes 20% of the full screen width.

Could you clarify "same as ** columns="1, 3, 1" *."?

@jlooper
jlooper commented Sep 8, 2015

Same as @emiloberg, I need clarification, please... I've seen this syntax before: columns="1, 3, 1*" or something similar (is that right?), and after thinking about it a bit, I see how it would make sense that 1,3,1 would translate to 20%,60%,20%. I would just have to get used to it. Normally when I see a number in the columns I'm assuming it's a pixel number, not a percentage, like in the docs. Is this other syntax documented?

@ligaz
Contributor
ligaz commented Sep 8, 2015

The magic happens in this code. The star notation comes from our XAML background and is described here as "weighted proportion of available space". In the CSS Grid Layout Module Working Draft spec it is referred as flexible length.

Here are some examples:

  • "2*, *" - means two columns (or rows) the first one taking ⅔ of the space and the second one ⅓.
  • "*, 3*, *" - 3 columns (rows), first taking 20%, second 60% and third 20% of the space.

We might want to align our naming and definition more closely to the CSS Grid Layout specification but I will let @hshristov comment on this 😄

@tjvantoll
Contributor

Woah, this thread is getting a bit epic. I'll chime in with a few replies to @hshristov:

<GridLayout columns="*, *" />
will give you the same as columns="50%,50%". Is there something I'm missing?

The problem is this syntax is very confusing to people with zero XAML experience (aka me, @jlooper, etc), as CSS has no concept of this syntax currently. I love the idea with aligning our conventions with the CSS grid layout spec moving forward.

What bugs me in the flexbox layout is that it is supposed to be used without changing the html. I'm by no means html, css expert - quite the opposite so please correct me if I'm wrong. You just mark one element as flexbox container and that's it. But in the native platforms (like Android and WU) elements cannot be added everywhere. Only ViewGroup (in Android) can have children.

So it will not be possible to specify the layout using only CSS. One should have something like FlexboxLayout element somewhere in the XML.

The advantage of flexbox is not that you have one less element in your visual tree, because in HTML you'll basically always have a <div> that acts as your flex container. The advantage is that you can control everything about your layout in CSS rather than your markup. The idea is that using flexbox keeps your markup clean, as layout “things” (height, width, orientation, etc) stay in CSS and not HTML. This is especially useful when you need to handle multiple screen sizes. For instance, if you want to layout elements differently on phones and tablets, you don't need to change your markup—all you need to do is add a media query. That same use case is really hard to accomplish in NativeScript today.

Anyways I think the argument for and against adding flexbox to NativeScript is outside the scope of this issue. Although I'm pro-flexbox because I come from a web development background, I have some big concerns about maintaining and documenting two different layout mechanisms.

For now, having syntax like columns="50%,50%" would my web developer brain happy, even if it's functionally equivalent to columns="*, *".

@emiloberg
Contributor

For now, having syntax like columns="50%,50%" would my web developer brain happy, even if it's functionally equivalent to columns="*, *".

👍

@hshristov
Member

@jlooper sorry but this markup continue to mess up my posts ;)
I meant

columns="1*, 3*, 1*" 

not "1, 3, 1". As you correctly pointed when there is no star - it is fixed column.

@ligaz I've checked Grid specification before we did our layout and I think we can align both but in CSS. Right now setting GridLayout options (rows/columns) is not allowed in CSS and if we allow it we will use the same CSS names. Does this sounds good?
As for the star issue - I guess it is not so known but the Grid CSS specification defines 'fr' which is as meaningful as star :)

@tjvantoll that is the point - to bring up discussion :)
For columns="50%, 30%" it could be done but only after we make percent based layout. The point is both columns sums up to 80% of the available size so in the end we need % layouts if we are going to support this case.
As for the flexbox layout - I'm not against it. On the contrary. I want to see as much cases as possible before we commit of doing it. I understand its advantages but like I said I don't see how it will fit in our XML.

For example in HTML you place div somewhere, name it and then specify its flexbox parameters in the CSS. But in our XML you don't have 'div'. Only elements that inherits from LayoutBase support adding views. So I want to see some possible code (xml & css) on how do you image it.

The way I see it - you will have few 'FlexboxLayout' tags in the XML and the in CSS you will specify their options as well as children options.

<Page>
    <FlexboxLayout id='rootContainer'>
        <Button id='element1' />
        <Button id='element2' />
        <FlexboxLayout id='innerContainer'>
            <SomeMoreControlsHere id='element3'/>
        </FlexboxLayout>
    </FlexboxLayout>
</Page>

When more complex layout is needed developer will have to nest more FlexboxLayout containers which goes against Google performance recommendation - to use as flat structure as possible.
That is why we have our GridLayout in the first place. It support fixed, auto and star (proportional) columns/rows. With it you can create complex layouts and even simulate all other layouts (not so convenient but still possible). With the addition of percent width, height, margins it will give even more flexibility.

But that's just my vision. Does it looks OK?
Do you have something different in mind?

Please comment on this and on percent based layout.

@hshristov
Member

@emiloberg Yep you understand it correctly. I think @ligaz explained '1,3,1' part better than me.

@emiloberg emiloberg referenced this issue in NativeScript/docs Sep 9, 2015
Closed

Document XAML-like sizing system #137

@tjvantoll
Contributor

For columns="50%, 30%" it could be done but only after we make percent based layout. The point is both columns sums up to 80% of the available size so in the end we need % layouts if we are going to support this case.

I'm definitely in favor of supporting %-based layouts. It's why I created this issue in the first place :)

I'll comment on flexbox too, but I worry that we're getting off topic from the main %-based layout discussion. For flexbox most of the good use cases can already be implemented in NativeScript. For instance flexbox is the de facto solution for vertical centering on the web, but NativeScript uses vertical-align in a way the web doesn't. Flexbox is also how I tend to structure my top-level layouts, but in NativeScript I'd use a <GridLayout> to handle that.

A <FlexboxLayout> might be nice, but mainly because it's familiar to web developers, and not necessarily because it solves use cases that the current NativeScript layouts don't. Thinking about this more, I'd say what I'd really like to see in NativeScript is the ability to control everything about a layout in CSS.

I'll give an example. Suppose I have a sidebar that I want to show on tablets and hide on small devices. On the web with flexbox and media queries this is trivial (see http://jsfiddle.net/tj_vantoll/xg2ujdsg/), but in today's NativeScript the implementation would be crazy hard. Even if we supported media queries, I'd still have to create multiple XML files because I can't change orientation, rows, or columns in CSS.

There are other uses cases that aren't tied to screen sizes too. Imagine I have two states in my app, and I have some way to toggle between them. In NativeScript if I wanted to dynamically change rows or columns I'd have to do it with some messy JavaScript. On the web I'd just toggle a CSS class name on some arbitrary element and then handle everything in CSS.

@ligaz
Contributor
ligaz commented Sep 10, 2015

There is one issue I see if we go down the percentage road. What should be the expected outcome of the following definition: 50, 50%, 50%?

In the star world this means that the second and the third things will get 50/50 of the available space after the first things occupies 50 pixels of it. In the web world isn't those 50% be calculated based on the whole available space and not the one that is left after all other elements are measured?

@tjvantoll
Contributor

In the web world isn't those 50% be calculated based on the whole available space and not the one that is left after all other elements are measured?

It depends. In a more traditional web layout yes, and you'll end up with a container that takes over 100% of the available width: http://jsfiddle.net/tj_vantoll/jew61hhc/.

However flexbox works differently, and actually works the same way you describe XAML working: http://jsfiddle.net/tj_vantoll/5xo5fgzn/.

I would want exactly the behavior you describe in your comment. Enabling those types of layouts is one of the reasons flexbox was created for web layouts. Plus you almost never want a container to take up over 100% of the available width.

@hshristov
Member

The way I've implemented percent based width/height is like you set width/height explicitly but in a later time. So if I add support for % support for columns/rows it will be like in the traditional web layouts. But you can still use the star notation.
Does this sounds good?

@tjvantoll
Contributor

Just so I understand, suppose I use columns="50, 50%, 50%" on a 1000px-wide device. Would the column widths be A) 50, 500, 500, or B) 50, 475, 475?

I think B is the preferred behavior here, but I can live with A if B is difficult to implement. I think most of the time people are going to do simple things like columns="30%, 70% anyways.

@hshristov
Member

There won't be % support for columns="50%, 50%".
% values will be supported only for width, height and margins. If you need to evenly distribute the remaining space of grid columns - use columns="50, *, *". Then on 1000px-wide device you will get 50, 475, 475.

@ligaz
Contributor
ligaz commented Sep 11, 2015

If users wants to use percentages over stars why not allow it. Under the hood we will still use the star notation, just the parsing will be changed to support % as well.

@hshristov
Member

@ligaz you mean to allow setting % instead of * which will mean the same (e.g. fraction from the remaining free space)?
I see problems with this approach. What should happen if the sum of column percent is not exactly 100?
How should I convert it to stars?

@emiloberg
Contributor

Percentage or XAML-like-sizing really should be usable in AbsoluteLayouts as well.

To my knowledge, it's not possible to, in any, to the following today:

Setting width to 100% of available width (which is the entire screen's width)

<AbsoluteLayout width="*" height="100"> 
# or like
<AbsoluteLayout width="100%" height="100"> 
@hshristov
Member

@emiloberg at the moment width and height default values are NaN which means that size is based on the desired size of the view and its horizontal and vertical alignments. So if you want to change the width from 50 to auto (e.g. 100%) - just set it to Number.NaN.

@emiloberg
Contributor

@hshristov, Hm. Not totally sure I understand you.

But. If I don't set the width/height (or set it to Number.NaN), the layout will take as much space as it "feels like it needs", which is depending of the content inside the layout, whereas I want to set it to 100% of the available screen.

@hshristov
Member

@emiloberg This will be available when we implement % based support for layout properties.

@x4080
x4080 commented Sep 28, 2015

I think we absolutely need the layout view can be "stretch" or "flex", if not we have to use orientation listener and manually resize every view, and it should be trickle down inside the layout

I tried that after page, 1st absolutelayout is 100% width and height (i tried with setting background-color), after that its just sizing what the ui "needed"

Can we see it in 1.4 ?

@N3ll N3ll added this to the 1.5 (Under Review) milestone Oct 12, 2015
@hshristov hshristov was assigned by hamorphis Nov 18, 2015
@hshristov hshristov modified the milestone: 1.6 (Under Review), 1.5 Nov 23, 2015
@amjd
amjd commented Dec 1, 2015

+1. Really need this.

@mike-ense

When will we see some % functionality usable,

Is anything fairly testable today or in a week etc.?

Just a thought, CSS misses combinations e.g. 5% + 0.5em + 2px.

Also a % value may be relative to more than one thing, so that topic needs some care.

Just touched on this with NativeScript/docs#218 , as @vakrilov just mentioned.

@dragGH102

+1 for % support

@vchimev vchimev closed this Jan 19, 2016
@NathanaelA
Contributor

@vchimev, Vasil, you marked this as Done, is their an actual pull request for this somewhere?

@NathanaelA
Contributor

Ok, I found then #1257 and #1348.

This was referenced Feb 10, 2016
@asiby
asiby commented Nov 14, 2016

Hi guys. I am new to {N} but not to mobile app development. I am thinking of something that may help anyone who to give this a try because I am not 100% comfy with plugin development yet.

What if you apply layout constraints that can actually act as max-width, max-height, min-width or min-height. Those do exist in both do have the equivalent of those.

Thoughts anyone?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment