Resize box based on Text

Started by sjenks, July 22, 2015, 02:36:44 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sjenks

Hi there,

I want to be able to resize a box (Shape) based on the amount of text that is in it. 

So say I have a box that contains:

Sample A. Sample
1234 Main St
Apt 1
City, State, Zip

And the box fits perfectly around this. But then I want to add a line. Ex.

Sample A. Sample
Company
1234 Main St
Apt 1
City, State, Zip

I want the box to expand with this extra line instead of having the text fall out of the box.

Thanks

Paul Herber

Open the shape's shapesheet and set the Height cell to
= GUARD(TEXTHEIGHT(TheText,Width))
Electronic and Electrical engineering, business and software stencils for Visio -

https://www.paulherber.co.uk/

sjenks


Hey Ken

   While we're talking about resizing boxes based on text size, is there any way to automatically set the width of the box to the width of the widest word?  Yes, there's TEXTWIDTH(TheText), but that gives you the width as if the text were all one line.  What about only the widest word?

   I wrote a provisional VBA solution that does it, but it's not pretty.  In the ShapeExitedTextEdit event, do this:

1)   Create a temporary shape;
2)   Put "=TEXTWIDTH(TheText)" into its Width shapesheet cell;
3)   Put the original shape's text into the temporary shape, replacing all spaces with carriage returns;
4)   Copy the resulting width value from the temporary shape back into the original shape;
5)   Delete the temporary shape.

   And there's more to it than that, such as copying the text characteristics from the original shape to the temporary shape, including font, font size, bold, character spacing, etc. so that the width of the temporary shape's text is the same as the original.  Well, almost the same; there's always some jiggle that requires adding a smidgen or else the last letter gets wrapped.

   As I said, it's not pretty.  There's got to be a better way!

   - Ken

Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

vojo

why vba???

Why not a subshape
   - user.tw = textwidth(theText)
   - width = guard(user.tw)

embedd that in a bigger shape
  - user.test = if (<subshape width> > <shape width>, <shape width>/<subshape width>, 1
  - font pt= guard(user.test * <default font pt>)

So basically as the subshape width grows BUT less that shape width, font pt stays at default
Once subshape width is larger than shape width, font pt might go from 18 to 16 to 14 to etc
Idea being subshape does not grow beyond shape width.

works on 2003

Hey Ken

Vojo:

   Interesting approach, but shrinking the font is not an option, especially now that I've taken to wearing reading glasses.  ;- )

   To provide a little more background, my boxes typically have a handful of words in them, but often become quite verbose.  (Sample here.)  Shrink things too much and it quickly becomes unreadable.  That becomes an issue when I do presentations with WebEx or use a projector with crappy resolution—or forget my glasses.  No, the text size must remain the same.

   More importantly, the goal here (as always) is to exert as little effort as possible when building a drawing.  Like sjenks not wanting text falling out the bottom of the box (which requires taking the time to re-size the height), I don't want to take the time to re-size the width.  Yes, it's only a couple of seconds, but the models I build typically have hundreds if not thousands of boxes.  It stacks up.  The compromise I made is to use a standard height and the widest word to set the width, and that is good enough for maybe 75% of the time.

   I also have a way to address the bulk of the remaining 25%, which is to use VBA to automatically widen the width further such that the height still remains the same.  In the verbose case where that doesn't do the trick, okay I'll stop and adjust the box.  There aren't that many of them, and I'm not sure what could be done automatically in that situation anyhow.

   Thanks for the thought though.

   - Ken


Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

vojo

shrinking the font is so the shape doesnt fill half the screen.   If you want to grow it forever, have at it

RE simple:   Security issues aside, I VBA can be tedious to set up.   You will also need a trigger in the shape to launch the
macro (maybe in eventfx cell).   Since that cell defaults to empty, you are back to making custom shapes

Good luck

Hey Ken

   This is sounding like a case of different strokes for different folks.  I'd call VBA "fun," not "tedious."  And I need VBA enabled as it is, so there's no additional security risk.

   Yes, I've had shapes with text that fills half the screen.  It's sometimes the nature of the beast.  But at least it's readable.  ;- )

   As I mentioned above, I use the ShapeExitedTextEdit VBA event to trigger the automatic resizing.  I couldn't use the EventXFMod shapesheet event because that would itself require triggering, such as manually changing the size of the shape.  And lest we forget, that's what I'm trying to avoid; and if it requires a custom shape, so be it.  All the shapes on my stencil are already custom anyhow.

   So I'm left with the original question: Is there an easier way to automatically resize the width of a shape to accommodate the widest word?  I was hoping for something akin to Paul's elegantly-simple suggestion for adjusting the height.

   Thanks,

   - Ken




Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

vojo

fair enough

one question:   So when the user is playing with the shape, how do you trigger the VBA? 
                       Are you saying actually have the VBA trigger to launch the routine?
                       If you are going to that level, why not have VBA adjust the box/font/etc
                       Not sure why doing this is "ALL VBA" created an issue for you...if VBA, in for a miligram, in for a megaton

wapperdude

The only way that comes to mind to discover that widest word would be to search thru the text string, capture the number of characters between consecutive spaces, keep the largest, set box width to that value.  But, I have no idea if that's possible.

Just a thought...perhaps will stir up a practical solution.

Wapperdude
Visio 2019 Pro

Hey Ken

Vojo, Wapperdude:

   Thanks for the comments and questions.  Some answers...

   I trigger the VBA using the ShapeExitedTextEdit VBA event.  Once you're done diddling the text in a shape, the event fires and the VBA event handler steps in and fixes the width.

   Unfortunately, I could not go to an all-VBA solution because of the complexity of determining the width of a word.  There are so many variables involved (font, font size, font scale, letter spacing, style, case, etc.) that you can't just count characters.  So I had to cheat and use a hybrid VBA/shapesheet solution. 

   The heart of the cheating is the TEXTWIDTH(TheText) shapesheet function.  It already takes into account all the aforementioned variables, except it does it for the whole string.  So all I had to do was break the string down into individual words.  Here's how I did it:

   When the ShapeExitedTextEdit VBA event fires, I add the following shapesheet cells to the shape:

User.Text = "=SHAPETEXT(TheText)"
User.Index = "=0"
User.Indexed = "=INDEX(User.Index,User.Text,"" "",CHAR(1))"

   (Actually, they're already in the shapesheet when I drop it off the master, but I digress...)  Next, I create a temporary shape with the same stencil master, set all the aforementioned font-ish variables to match the original shape's, then add the following shapesheet cell:

User.TextWidth = "=TEXTWIDTH(TheText)"

   I then use a VBA loop that keeps incrementing User.Index in the original shape.  For each increment, it moves User.Indexed into the temporary shape's text, meaning all that's there is a single space-delimited word.  The VBA then references User.TextWidth in the temporary shape and saves off the highest value it finds.  When the loop hits the ending CHAR(1), it deletes the temp shape and moves the highest width value into the original shape.  The height is automatically controlled by Paul's technique.

   Here's the best part of the code that does it:



Dim MaxWidth As Single

Set TheShadowBox = ActivePage.Drop(TheMaster, 0, 0)
' set font &c to what the box is
TheShadowBox.Cells("Char.Font").Formula = TheBox.Cells("Char.Font").Formula
TheShadowBox.Cells("Char.Size").Formula = TheBox.Cells("Char.Size").Formula
TheShadowBox.Cells("Char.FontScale").Formula = TheBox.Cells("Char.FontScale").Formula
TheShadowBox.Cells("Char.LetterSpace").Formula = TheBox.Cells("Char.LetterSpace").Formula
TheShadowBox.Cells("Char.Style").Formula = TheBox.Cells("Char.Style").Formula
TheShadowBox.Cells("Char.Case").Formula = TheBox.Cells("Char.Case").Formula
TheShadowBox.AddNamedRow visSectionUser, "TextWidth", 0
TheShadowBox.Cells("User.TextWidth").Formula = "=TextWidth(TheText)"

MaxWidth = 0
TheBox.Cells("User.Index").Formula = 0
Do While TheBox.Cells("User.Indexed").ResultStr("") <> Chr(1)
  ' get next word
  TheShadowBox.Text = TheBox.Cells("User.Indexed").ResultStr("")
  If TheShadowBox.Cells("User.TextWidth").Result("Inches") > MaxWidth Then
    MaxWidth = TheShadowBox.Cells("User.TextWidth").Result("Inches")
    End If
  TheBox.Cells("User.Index").Formula = TheBox.Cells("User.Index").Formula + 1
  Loop

TheBox.Cells("Width").Formula = MaxWidth
TheShadowBox.Delete



   That's it.  As I said, it's not pretty.  But it works, sort of.  I must be missing an initialization in the temp shape because every now and again it's off by some almost-negligible fraction of an inch.  So I add a smidgen and promise myself that someday I'll track down the reason why.  I used Char(1) as an ending delimiter because it's not the sort of character you find in a shape's text, yet the shapesheet still recognizes it as a character (which is not true of all the early ASCII characters).

   See why I want to find a better solution?

   - Ken




Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com

vojo

have at it

Good luck with that

wapperdude

Hi Ken,

The widest word thing was an intriguing problem.  I agree that you need to parse the text and send it to a secondary shape in order to capture the width with formatting.

I used the "space" as the delimiter between words.  Capture the text between delimiters.  Found the width.  Compared to previous width and kept the widest.  The last word doesnot necessarily have a trailing space, so, it becomes a special case.  Once, all words have been examined, then, the main shape width is set as discovered.

So, what to do about the secondary shape?  Could delete it.  But, that becomes problematical for the code, since there must always be a shape to which the word is sent.  I opted to make a custom shape.  The main shape with all of the text is converted to a group.  The secondary shape is added to the group, and is behind the main shape, never seen.  No deletion necessary.  Need more text boxes?  Copy and past.  To run the macro, a shape must be selected of course.

The secondary  shape has the font and character size slaved to the main shape.  The character size is bumped up by 1 pt.

On a very small test sample seems to work.  See attached.

Wapperdude
Visio 2019 Pro

wapperdude

Made a change:  macro writes to main shape User.Row_3 rather than directly to the Width cell.  Now, the width cell contains a SETATREFEXPR() formula which allows the user to stretch the width of the shape, but prevents shrinking to less than the widest word.  Consequently, the resizing is NOT automatic.  This might be a convenient alternative.

Wapperdude
Visio 2019 Pro

Hey Ken

Wapperude:

   An interesting approach, not too far removed from what I did, but with some significant differences.

   I like the idea of not being able to contract the shape width to be less than the widest word.  I'll be borrowing that technique, thank you very much.

   Also liked the idea of using the Characters object.  Makes me wonder which is faster: that, or indexing in a shapesheet cell like I did.  Not sure it matters, given how nothing else important is executing at the time.

   I'm also not sure why it becomes problematical to delete a shape.  In fact, I'd prefer deleting it for performance reasons, especially when publishing a drawing as a web page.  That's because the more complex the shape, the longer it takes to display the HTML in a browser, which is something I do a lot.  I once made a supershape combining all the cute little human shapes into a single shape—such as a guy sitting at a monitor, a board room full of people, a help desk guy, etc.—and you chose which one to display by right clicking.  Worked fine in Visio, but took forever to display in a browser, especially when there were a lot of them on the same page.  I see the same issue could arise with not deleting your temporary shape because you've effectively doubled the number of shapes on a page, thereby doubling the time it takes to render.

   If there is a good reason to avoid deleting the temp shape, perhaps a single shape could be hidden on a hidden page and used for sizing all shapes (something I already do for font and font size).  But before going thru all that trouble, why do you find deleting problematic?

   - Ken



Ken V. Krawchuk
Author
No Dogs on Mars - A Starship Story
http://astarshipstory.com