How to vertically center multi line text area

TDLR; I WANT TO AUTOMATIZE CENTER ALIGNMENT OF MULTI LINE TEXT AREA

Hi!

Is it just me or does it feel impossible to automatically determine the vertical center of a multi line text area?

I would like to achieve a universal center alignment for a multi-line TextArea. I am developing for multiple devices with very different screen sizes. Simply put, my goal is to make sure that the center is truly the center. I can easily do this if the text is single-line by calculating the fontHeight and shifting it negatively from half of the screen height. However, when the mentioned text becomes multi-line on a device, using half the font size for the shift is no longer accurate. I would like to automate this process somehow, maybe by determining how many lines a TextArea will have on a given screen, or finding out the height of the TextArea itself, or any other method. I tried setting the justification to Graphics.TEXT_JUSTIFY_VCENTER, which centers the text vertically, but it right-aligns the text.

Is there a way to automate this?

  • instead of 

     Graphics.TEXT_JUSTIFY_VCENTER

    try

     Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER

    to center both ways

  • Thank you very much for the response; this solved the situation.

    However, I would like to ask where I can learn more about this syntax and why it works when the :justification symbol for a TextArea specifies only 'Graphics.TextJustification or Lang.Number' in the API. How can I find out the syntax for using the '|' vertical line from this?

    developer.garmin.com/.../TextArea.html

  • both TEXT_JUSTIFY_CENTER and TEXT_JUSTIFY_VCENTER are text justifications, and the "|" is "or" so you are or-ing the two together.

  • Unfortunately it's not really documented, but Graphics.TextJustification is (sort of) a bitwise enumeration.

    In a normal bitwise enumeration, each value is a "flag" corresponding to a single bit, and multiple "flags" can be combined using bitwise OR ("|").

    Here's a fake example:

    COLOR_RED =    1 (binary 00000001)
    COLOR_GREEN =  2 (binary 00000010)
    COLOR_BLUE =   4 (binary 00000100)
    COLOR_YELLOW = 8 (binary 00001000)

    (I put leading 0s in the binary values for clarity, but it doesn't make a difference.)

    In this case, each enum value is a power of 2 which corresponds to a single binary digit (bit).

    Red is the 1st bit from the right, green is the 2nd bit, blue is the 3rd bit, and yellow is the 4th bit. When you perform bitwise OR on two numbers, each bit that's set (equal to 1) in either of the two numbers will be also be set in the result. All other bits in the result will be 0.

    e.g.

    var x = COLOR_RED;  // binary 0001
    var y = COLOR_BLUE; // binary 0100
    var z = x | y;      // binary 0101

    In this case, z represents "COLOR_RED or COLOR_BLUE".

    Another example:

    var t = COLOR_RED | COLOR_GREEN | COLOR_YELLOW; // binary 1011

    in Connect IQ, TextJustification is a combination of a normal enumeration (which is just a list of numbers) and a bitwise enumeration (where flags can be combined with bitwise OR).

    [https://developer.garmin.com/connect-iq/api-docs/Toybox/Graphics.html#TextJustification-module]

    Here's the binary representation of each value:

    TEXT_JUSTIFY_RIGHT =   0 (decimal) = 00000000 (binary)
    TEXT_JUSTIFY_CENTER =  1 (decimal) = 00000001 (binary)
    TEXT_JUSTIFY_LEFT =    2 (decimal) = 00000010 (binary)
    TEXT_JUSTIFY_VCENTER = 4 (decimal) = 00000100 (binary)

    As you can see, TEXT_JUSTIFY_RIGHT doesn't have any bits set, so TextJustification isn't a normal bitwise enum. What's happening here is that it doesn't make sense to combine RIGHT, CENTER or LEFT with each other, so those 3 values are just "normal" enum values which don't necessarily correspond to a single bit. VCENTER does correspond to a single bit, as it can be combined with any one of RIGHT, CENTER, or LEFT.

    So in this scheme, the bits are laid out as follows:

    00000???
          ^^ 2 bits for (horizontal) right/center/left justification
         ^   1 bit for vertical centering
         

    So:

    Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER
    = 1 | 4
    = 0001 | 0100 (binary)
    = 0101
    = 5 (decimal)

    If I were writing a function to interpret the kind of TextJustification value that would be passed into a CIQ Graphics API function, it could look like this:

    function printTextJustification(justification as Graphics.TextJustification or Number) as String {
        // the rightmost 2 bits are used for horizontal justification
        var horizontalJustification = justification & 3; // 3 (decimal) = 0011 (binary)
        // the 3rd bit is used for vertical justification
        var verticalJustification = justification & Graphics.TEXT_JUSTIFY_VCENTER; // 4 (decimal) = 0100 (binary)
    
        // "&" = "bitwise AND". Given "z = x & y", z will have a bit set (equal to 1)
        // if both x and y have the same bit set
    
        var justificationString = "Horizontal justification: ";
        switch (horizontalJustication) {
            case TEXT_JUSTIFY_CENTER:
                justificationString += "Center";
                break;
            case TEXT_JUSTIFY_LEFT:
                justificationString += "Left";
                break;
            case TEXT_JUSTIFY_RIGHT:
                justificationString += "Right";
                break;
        }
    
        justificationString += "| Vertical justification: ";
        if (verticalJustification != 0) {
            justificationString += "Centred";
        } else {
            justificationString += "None";
        }
        return justificationString;
    }

    EDIT: textJustificationToString() would be a better name than printTextJustification(), but the forum won't let me edit the code.

    Of course you don't really have to understand any of that to use the TextJustification "enumeration".

    It sure would be nice if the docs said that TEXT_JUSTIFY_VCENTER can be combined with any one of the other three using "|" tho.

  • More on bitwise operations ("|" / OR, "&" / AND): https://en.wikipedia.org/wiki/Bitwise_operation

    I can't find a great comprehensive resource on bitwise enumerations right now, probably because they're all language-specific. Here's a couple of ok ones:

    https://www.c-sharpcorner.com/article/understanding-bitwise-enums-in-c-sharp/

    timdeschryver.dev/.../flagged-enum-what-why-and-how

    I will say that the main reason to use bitwise enumerations is efficiency.

  • This was just amazing to read. Thank you for your time, knowledge and help!

  • No problem! I will also note that hexadecimal (base 16) is very often used to describe bitwise enums (and other values where individual bits are significant) because each digit corresponds to exactly 4 bits, so it's relatively easy to convert back and forth (or just to think of a hex value in terms of bits.) It's also sometimes useful when dealing with numbers that are known to occupy a certain number of bytes (1 byte = 8 bits), such as memory addresses. (That's why a hex editor, which allows you to edit the raw bytes of a file, shows memory addresses and data in hex.)

    e.g.

    0x01 (hex) = 0b00000001 (binary)
    0x02 = 0b00000010
    0x04 = 0b00000100
    0x08 = 0b00001000
    0x10 = 0b00010000
    0x20 = 0b00100000
    0x40 = 0b01000000
    0x80 = 0b10000000

    In Monkey C (and many other languages, including Javascript and Python), the "0x" prefix denotes a hex value (e.g. 0xFF = 255). Python and Javascript (but not Monkey C) also use the "0b" prefix for binary.