How to manage and position objects when a stack is resized
When you are the only person using your stacks, their layout and size often does not matter much. But as soon as you distribute them, be it as full blown application, or just as quick fun thingy for a friend, you will need to manage object positions and sizes for any stack size the user chooses.
This is a very basic introduction to custom resizing, and of course only one way to go about things. Make sure to read the relevant dictionary entries and make up your own mind about how you want to your application to behave when the window is resized.
You can download the sample stack from this url: https://tinyurl.com/y9wvhkes
Create Layout
I have created a simple example layout. Even tho it only has few controls, It'll show you most basic resize Operations. For example, I'll want the search field to be resized in width, but not in height. The list field should be completely resized, while the buttons should only be moved. Finally, there's two graphics, a dark grey rectangle, and a light grey line that makes the border to the list field look smoother. Those are adjusted only horizontally, similar to the search field.
Thinking about position dependency
Before we go and create the script, I should think about what objects depend on what other object. For example, the list fields top border position depends on the rectangle graphic behind the search field and buttons. Meanwhile, the buttons position depends on the search field width, one after another. Finally, the two graphics depend on the width of the stack (actually the card, as I explain later).
This means I need to make two mental groups of the objects. First I'll deal with the two graphics and the list field. I need to start on the top left, because that point is not going to move and always be 0,0. Similar to that, the top leftmost object should go first, in this case the rectangle graphic (Nr. 1 above). After I have adjusted the width of that one, I'll position the line graphic (Nr. 2) at the bottom of the rectangle, and make the list field follow next (Nr. 3).
Similar, I'll start with the search fields topleft position (Nr. 4), which remains unchanged. Then I calculate the new width of the field, and move the buttons relative to that (Nr 5 to 7).
About resizeStack
The resizeStack message is sent to the current card, then travels trough the message path. This is important to know, because if you put a resizeStack handler into your stack script, then you will run into troubles, as soon as you have substacks.
Why is that? As seen in the image above, the mainstack will react to unhandled resizestack messages. Because of that, it is good practice to always put resizeStack handlers into the card script. Similar, if you have several cards in your stack, with different layouts, the resizeStack handle might produce a runtime error, because it would try to move objects that are not on the current card.
Handling the graphics and the field
The code to resize the graphics and the fields looks like this:
on resizeStack cardWidth, cardHeight
set the width of graphic "rectangle" to cardWidth
set the topleft of graphic "rectangle" to 0,0
set the points of graphic "line" to the bottomleft of graphic "rectangle" & return & the bottomright of graphic "rectangle"
put 0 into listFieldLeft
put the bottom of graphic "rectangle" +1 into listFieldTop
put cardWidth into listFieldright
put cardHeight -14 into listFieldbottom
set the rect of field "list" to listFieldLeft,listFieldTop,listFieldRight,listFieldBottom
end resizeStack
As you see, I need to offset the bottom of the field by 14 pixels, because otherwise it'd vanish behind the resize grabber of the stack.
Note that I do not use "lock screen" and "unlock screen" as I would have done in other handlers. resizeStack behaves special, and handles all resizing and positioning at once.
Handling the field and the buttons
This code looks like this:
on resizeStack cardWidth, cardHeight
put the topleft of field "search" into savedTopLeft
put the left of field "search" into theLength
add the width of button "button 1" + 3 to theLength
add the width of button "button 2" + 3 to theLength
add the width of button "button 3" + 9 to theLength
set the width of field "search" to cardWidth - theLength
set the topleft of field "search" to savedTopLeft
set the left of button "button 1" to the right of field "search" + 3
set the left of button "button 2" to the right of button "button 1" + 3
set the left of button "button 3" to the right of button "button 2" + 3
end resizeStack
I could have reduced the code complexity, by starting with the buttons, then going right, instead of going left from the field. However, it's often easier to think about resizing only from one direction, that is why i started out going the other way. So how could the same result be achieved, if we'd started on the right?
on resizeStack cardWidth, cardHeight
set the right of button "button 3" to cardWidth - 9
set the right of button "button 2" to the left of button "button 3" - 3
set the right of button "button 1" to the left of button "button 2" - 3
put the topleft of field "search" into savedTopLeft
put the left of button "button 1" - the left of field "search" -3 into theWidth
set the width of field "search" to theWidth
set the topleft of field "search" to savedTopLeft
end resizeStack
Much nicer to follow, in my opinion. So don't forget to be flexible, if your first positioning approach is starting to get too complex.
Combined code
To combine both behaviours, all we need to do is to make only one resizeStack handler with both code snippets:
on resizeStack cardWidth, cardHeight
--graphics and list field
set the width of graphic "rectangle" to cardWidth
set the topleft of graphic "rectangle" to 0,0
set the points of graphic "line" to the bottomleft of graphic "rectangle" & return & the bottomright of graphic "rectangle"
put 0 into listFieldLeft
put the bottom of graphic "rectangle" +1 into listFieldTop
put cardWidth into listFieldright
put cardHeight -14 into listFieldbottom
set the rect of field "list" to listFieldLeft,listFieldTop,listFieldRight,listFieldBottom
--search field and buttons
set the right of button "button 3" to cardWidth - 9
set the right of button "button 2" to the left of button "button 3" - 3
set the right of button "button 1" to the left of button "button 2" - 3
put the topleft of field "search" into savedTopLeft
put the left of button "button 1" - the left of field "search" -3 into theWidth
set the width of field "search" to theWidth
set the topleft of field "search" to savedTopLeft
end resizeStack
Additional things to think about
There are some additional steps you will want to do, for the stack to always resize properly. For example, if the user is on another card in the stack, and resize the stack there, then your custom resize handler won't run. So you need to make it run whenever the user navigates to your card:
on preOpenCard
resizeStack the width of this card, the height of this card
end preOpenCard
Another thing to watch out for is, that the code can't set objects to a negative width. So if the user resizes the stack to a very small size, then a script error will occur in this example stack. To prevent that, make sure to set the minimal width and height of your stack to something sensible:
set the minWidth of this stack to 240
set the minheight of this stack to 180
When you're trying out resizing scripts, it can happen that an object is resized or repositioned strangely, for example to a huge negative location, where you can't see it anymore. So if you try to create resizeStack code, make a copy of all objects into some temporary stack. That way, if things go haywire, you can always delete all the objects, copy them back in, and restart from scratch.
Also, for complex stacks with a lot of objects, try to group objects that behave the same, or belong to each other in the resizeStack handler. For example, I could have grouped the three buttons together, and then only reposition that group, instead of each button individually. It'd have reduced 3 objects that I need to think about to just one. Of course that's not really a problem in this example stack, but for larger layouts, grouping stuff together can be a lifesaver.
Marilyn
Is there any way I can center a button (or graphic) on a card, so it will always stay in the center of the page when resized?
Right now I am looking at a button staying centered with even spacing on both the left and right sides. However, knowing how to center with even spacing from the top and bottom could be useful too!
Hanson Schmidt-Cornelius
Hi Marilyn,
try something like this:
// Replace "myButton" with the name of the button you want to be centred.
on resizeStack cardWidth, cardHeight
set the location of button "myButton" to cardWidth/2, cardHeight/2
end resizeStack
Kind Regards,
Hanson
Marilyn
Hi Hanson,
This "location" code was exactly what I needed. I put in /2 in the card width, then I guessed at the card height I needed. After a few attempts - perfect!
Thank you so much!
Marilyn
Marilyn
Hi Hanson,
I have another question. I have a simple text field (not a list).
How do I automatically resize the field/text when I resize the stack?
How do I stipulate a maximum and minimum text size?
Thank You,
Marilyn
Elanor Buchanan
Hi Marilyn
The best way to resize the field when you receive the resizeStack message, for example
set the width of field "my field" to the width of this card -20
set the height of field "my field" to the height of this card - 20
set the topLeft of field "my field" to 10,10
You can resize the text by comparing the formattedHeight and height of the field. The formattedHeight gives you the height needed by an object to display its full contents without scrolling so you can use this to caluclate the size you want the text to be.
I would suggest using custom properties of the field to store the miminum and maximum text heights, you can then ensure the text size never exceeds these.
I have attached a small example stack that I hope will help you.
Kind regards
Elanor
Note: You need to set the initial textSize of the field for this to work.
Marilyn
Elanor
I am now able to resize my text field, however, my text remains the same size.
I did not receive your attachment of the example stack. Could you resend the example so I can learn how to resize my text using the custom properties?
Thank you
Marilyn
Elanor Buchanan
Hi Marilyn
If you scroll up to the top of the lesson you should see the example under Attached Files.
Kind regards
Elanor
Marilyn
Elanor, or Hanson:
Is there any way I can resize my text in steps? The greater than, or less than works great, but I can't seem to find the sytax for in between sizes.
In other words, how can I code a field width that is between 65 and 80? See my coding below.
Thank you
My script i.e.,
on resizeText
put width of field "a" into taWidth
if taWidth < 65 then
set the textSize of field "a" to 10
else if taWidth > 65 and < 80 then
set the textSize of field "a" to 16
else if taWidth > 80 then
set the textSize of field "a" to 20
end if
end resizeText
Hanson Schmidt-Cornelius
Hi Marilyn,
you have a very small syntactical issue in your code. Try changing line:
else if taWidth > 65 and < 80 then
to
else if taWidth > 65 and taWidth < 80 then
Kind Regards,
Hanson
Marilyn
Hanson,
Thank you for the syntax correction. Now the coding works perfectly.
Gratefully,
Marilyn