Length Literals to bring in the New Year!

11 Aufrufe
Direkt zur ersten ungelesenen Nachricht

Stephen Chin

ungelesen,
31.12.2010, 04:03:1031.12.10
an visag...@googlegroups.com
Well, probably not what you were wishing for when you thought about what you wanted for 2011, but I implemented it anyway.

What is a length literal?  Here are a few examples:
12px
5mm
2cm
1.5em

They are similar to the Duration type, but applicable to a lot more places (think of how many different places you specify length or pixel values in most APIs).

So what other interesting things can you do with them?  You can:
add lengths: 5cm + 5mm
subtract lengths: 5cm - 5mm
divide lengths: 5mm / 2
divide lengths by lengths: 5cm / 5mm
compare lengths: 5mm < 5cm

This is all pretty basic, but the complexity comes in with different types of length.  How do you convert from pixels to inches?  What length is an em?  I looked at all the different length systems in common use today, and came up with what I think is a reasonable system.  Feel free to weigh in...

The Visage Length class supports 4 different classes of lengths:
  • pixel - Exact screen pixels.  This should not be used for layouts that need to be resizable or device portable.
  • dp - Density-independent pixels - Reference pixel for the target device.  Will scale to a whole or even fractional pixel value based on the device viewing distance and density.  This is approximately one pixel on a device with a density of 96dpi at arm's length.  For example:
    • Desktop - 28 inches away, .26mm pixel (96 dpi), 1dp = 1px
    • Medium Density Mobile - 17 inches away, .16mm pixel (160 dpi), 1dp = 1px
    • High Density Mobile - 17 inches away, .16mm pixel (240 dpi), 1dp = 1.5px
    • 55" HD TV - 10 feet away, .73mm pixel (35 dpi), 1dp = 1.5px
  • sp - Scale-independent pixels - Similar to dp, but also relative to the user font scale.  For the default font scale will be 1 to 1 with dp.
  • em - Typographic measure relative to the enclosing element's font height.
  • percentage - Percentage of the enclosing element's length or size.
The DP and SP units are inspired from the Android APIs.  DP is actually equivalent to the reference pixel concept in CSS, so it is really nothing new.  However, defining it as 160dpi makes a lot of sense (for mobile), just not for desktop.

SP units also have a CSS equivalent.  They are semantically similar to the new rem (root em) unit defined in CSS3.  It solves a fundamental problem with the em unit, which is the context sensitivity.  It is often difficult to figure out what size an em will show up as.  Also, simply reparenting an element specified in ems may change its size.

Inches, centimeters, millimeters, points, and picas are losslessly converted into density-independent pixels at a scale of 96dpi (preserving compatibility with CSS).  This is a pretty good approximation of print-format needs, so it should prevent gross resizing of pixels when inches become fixed to a physical unit.

Percentage is a special case...  it is entirely context dependent (on the container and field usage), so there is really no point converting it.

To make it possible to include these concepts in the language where there is no knowledge of the device metrics or scenegraph context, I introduced a concept of ComplexLengths.  Addition and subtraction of simple lengths may result in a new length object that cannot be simplified, such as:
println("complex = {5px + 5dp}");
This will literally print the following:
complex = 5px + 5dp

However, if an expression can be simplified, it will be:
println("simple = {%#s 1cm + 1mm}
simple = 11mm

Notice that I used a funky formatting symbol on that expression.  The Length type also implements Formattable with full support for width, precision, capitalization, justification, and alternate formats.  The alternate format for lengths is to choose an equivalent unit that has the shortest form (in this case mm).  The normal format method will simply use the base type (in this case dp) and print a decimal value to 6 significant digits.

When lengths are complex or simply incompatible, certain operations may not work.  For example, you can't compare px and dp, so this will throw an IncompatibleLengthException:
5px < 5dp

You could fix this by first converting them to compatible units like the following:
def dps = 5px.toDensityIndependentLength(1.5);
5px < dps

Notice that I passed in a density factor of 1.5.  This means 1px = 1.5dp, which would be appropriate for a very high resolution display (like a 240dpi WVGA phone display).

There are similar conversion methods for every combination that I thought makes sense...  although in practice, the one that will prove the most useful is the toPixels method.

So with all this unit madness, what do I expect people to really do?
  • API Designers: Use the Visage Length type anywhere a length or font size would normally be used.  When you get back a length, simply convert it to pixels (passing in the appropriate conversion factors) and let the Visage implementation do all the complex math.
  • Application Developers: Use lengths that make sense for the context you are working in...  DPs (or a compatible measure) most of the time.  EMs if you want to line up to the enclosing text.  SPs when you want something to scale with the user font preference.  Pixels when you really need to make sure it lines up with the device pixel grid (thin lines, etc.)  Mixing and matching lengths is fine too.
And why should this be a feature of Visage, and not the APIs built on top (maybe I should have started with this):
  1. Succinctness - It is really nice to simply be able to write 1mm rather than Length {value: 1, unit: LengthUnit.MM}
  2. Uniformity - CSS does lengths one way, Android another, JavaFX another...  and even in the best case you have platform differences (Mac/Windows, IE/Webkit) that further complicate things.  As long as you let Visage do the conversion of lengths, you can (mostly) ignore the underlying funky toolkits.

The code for all this Length literal goodness is checked in if you want to take a look.  Here is a link to the change list (which also includes a first pass at an Angle literal...  more on this soon):
http://code.google.com/p/visage/source/detail?r=00179720e5af01bd021ff2f46ed66b5312ae966d

Cheers,
--
--Steve
blog: http://steveonjava.com/

James Weaver

ungelesen,
31.12.2010, 09:11:1531.12.10
an visag...@googlegroups.com
Very nice, Stephen, and thanks for going to such great *lengths* to implement this :-)

Happy New Year to all on this list,
Jim Weaver

Robert Casey

ungelesen,
03.01.2011, 12:15:1203.01.11
an visag...@googlegroups.com

This and the new Color implementations sound great, Stephen. I am
glad to see that there is momentum on this project. Clearly some
skilled developers at work, such as yourself. Being a JavaFX Script
fan, I look forward to transitioning to Visage in the future.

Stephen Chin

ungelesen,
04.01.2011, 04:30:5904.01.11
an visag...@googlegroups.com
Thanks for the support, Rob.

The compiler work is pretty rough, so it is mostly just me (and
Jan-Hendrik) now.

Although the API work to wrap Android (once I get the basic pattern
down), and JavaFX 2.0 (once they release it) is something that everyone
can chip in on. More on this shortly...

Cheers,
--Steve

--
--Steve
blog: http://steveonjava.com/

Allen antworten
Antwort an Autor
Weiterleiten
0 neue Nachrichten