TextRectangle / Wrapping text.

Posted by Lasher on Sun 15 Aug 2010 03:39 PM — 20 posts, 81,120 views.

USA #0

The documentation for TextRectangle states:

"The text is clipped to be within the text rectangle. A window redraw is forced."

Is there a way to make this not happen? I just changed font size in a TextRectangle and MUD output is now truncated. Pretending I don't have the knowledge to change the plugin itself, as a user I can see no way to resize the TextRectangle to allow the text to fit and my only option is to change the font size back.

It would seem that the intuitive behavior for a user here would be to check "Auto-wrap to window size" in world configuration. With output redirected to a TextRectangle this setting appears to be ignored. I debated posting this last part in "bugs" but may be missing something, so any feedback appreciated.

Thanks!




Australia Forum Administrator #1
It's been a design feature of MUSHclient from early days that it never re-wraps text once received. The wrapping (at column 80 or whatever) is done on receipt of the text. If you subsequently change the font or text size (or more recently, the text rectangle dimensions) then the existing lines of text (stored internally as a linked list) simply either is truncated or displayed with a gap on the right.

This is mainly for performance reasons, since it is designed to allow up to 500,000 lines in the buffer, even resizing slightly or changing the font size slightly could result in a massive delay while it recomputes line endings for all those lines.

You have enough information in the GetInfo calls to recalculate the wrapping point yourself (the new Debug ("summary") even tells you how many character would fit in the window), so it can be done easily enough.

Thus, if you are planning to script a TextRectangle call, then you would need to follow it up with change to the wrapping point.

Of course, the doesn't address what would happen if the player subsequently changes fonts or font sizes.

However playing MUDs isn't really like using a word processor. Many MUDs I have seen expect at least 80 characters, and have things like "score", "who", and "train" commands pre-formatted at the server end to fit into 80 columns. Indeed SMAUG pre-wraps lines at 80 characters (the linebreaks are in the area files) so fiddling with the actual wrap column doesn't achieve a huge amount).

If the player changes fonts/sizes so 80 characters don't fit, they are going to get a degraded experience anyway, so I would suggest "don't do that".

What *could* be done is to have a plugin callback that detects a font or font size change (similar to detecting a window resize) and then the plugin could make a new text rectangle size. This new rectangle could be designed to fit at least 80 characters into it.

But you still have the issue that you may have lots of miniwindows nicely laid out (eg. maps) and that then they have to be moved around as well.
USA Global Moderator #2
I'm writing from my phone here, so I obviously haven't looked into much, but...

How is a text rectangle treated internally? Is it essentially a mini window? Nick suggests resizing the window on a text change event. Could one possibly trap the text resize event and then flush the window, reflow (like the chat plugin), and then refill instead?
Australia Forum Administrator #3
It's possible. It isn't really a miniwindow, but the main output window. Rewrapping hundreds of thousands of lines could be slow, there would be all the style runs to look at, you would need to work out where to break the lines, the line breaks might be in the middle of a style run (eg. if the break point was in some white-on-black text) so a new style run would have to be created, and half of the text put in the old one and half in the new one.

Honestly, is it worth the effort? This isn't a word processor. You usually have 80 columns of text from many MUDs, and once you make up your mind, you usually stick to the same font and font size.
USA Global Moderator #4
Nick Gammon said:

Honestly, is it worth the effort? This isn't a word processor. You usually have 80 columns of text from many MUDs, and once you make up your mind, you usually stick to the same font and font size.


What effort? The lua to rewrap lines has already been written. All that's left is a small routine to copy and paste the history buffer. ;)

I get what you're saying, of course. This is such a rare event, why bother. I just know that I'd notice little differences like this.
Amended on Fri 20 Aug 2010 12:53 AM by Fiendish
USA Global Moderator #5
Also, I think that anything that I want to do is worth it. To me at least. :D
USA #6
You can probably roughly simulate a font-change event with an OnPluginTick listener that checks the text font every tick.
Australia Forum Administrator #7
Fiendish said:

What effort? The lua to rewrap lines has already been written. All that's left is a small routine to copy and paste the history buffer. ;)


Well, you can bookmark lines for one thing. What happens to bookmarks once you change all the line boundaries?

Internally the lines also have hyperlink information in them (so when you click on a hyperlink it knows what to do).

So it isn't already done, per se. Look, if it could be done in a dozen lines of code do you think I would be resisting, just to annoy everyone?

And your comment doesn't address the time issue. I said it "could be slow". You haven't said "well I don't care", or "speed it up", or "put up a warning and let the player choose".

If you are prepared to put up with certain limitations (eg. losing bookmarks, losing hyperlinks) then you can do it yourself. Just write a Lua routine to copy the entire buffer into a big Lua table, delete all from output, and then "ColourNote" it all back, rewrapping as you go.
Netherlands #8
Nitpicking that for the hell of it: reflowing like that would lose the time information of when the original lines came in! That's like the most important line data other than the actual line! (And probably also about the only other, but yeah, details...)


I am totally against it, by the by. It would kill the speed which is one of my main reasons for loving MUSHclient, and second, telnet was never something meant to do in-place resizing of the output 'device'. Hell, look at terminals and how putty handle them. Admittedly, they are far more fancy things, and nowadays they and certain programs have a bit of support for resizing, but if you look at a simple terminal, you'll find no reflowing is being done, since it is pretty much impossible to do so reliably. Some games have ASCII maps, others use simply ASCII art to put down tables or other framed information. Once you start reflowing, that stuff starts breaking into total ugliness.
Australia Forum Administrator #9
Thank you Worstje.

You have pretty-much hit the nail on the head. The original design was supposed to handle the fact that, on MUSHes in particular, they sent down long lines, and you needed to wrap them (at the nearest space) to make them readable.

However many, many MUDs assume 80 characters (who lists, scores, maps, even room descriptions).

I can understand that the width of the rectangle might need changing to suit a new font size, but arbitrarily rewrapping all existing text? I think not.

Another issue is the buffer size. Say you had the buffer full (to whatever your limit was, say 10,000 lines). Then you make the borders narrower, so it becomes 12,000 lines. Do 2,000 lines get discarded? What if you wrap back to a wider width? Do they somehow magically come back?

One viable option for MUD plugin developers is to consider actually making everything a miniwindow. That is, omit absolutely everything from output, and funnel it into different streams (eg. chat, combat messages, auction messages, xp gain, status bars, xp bars, etc.)

Then the behaviour of the default output window becomes irrelevant.
USA Global Moderator #10
Nick Gammon said:

Well, you can bookmark lines for one thing. What happens to bookmarks once you change all the line boundaries?

Internally the lines also have hyperlink information in them (so when you click on a hyperlink it knows what to do).
True, though perhaps not too difficultly surmounted.
My chat plugin keeps track of the start of messages so that users can click anywhere on a message to copy the whole thing. When lines rewrap, the positions get updated. An enterprising scripter could do the same for bookmarks and links. Bookmarks associate with the start of a message. Hyperlinks I'm not sure yet, though I bet it could be done.

Quote:
Look, if it could be done in a dozen lines of code do you think I would be resisting, just to annoy everyone?

Resisting what? I'm not saying that YOU should do any of this. :)
I'm speaking only in the hypothetical realm of what's doable given a few of the right hooks (to the head). I didn't think of it before, but I guess you need to be able to assign bookmarks and hyperlinks to the main output in lua. Are there other things than that raw ability this would need?

Quote:
And your comment doesn't address the time issue. I said it "could be slow".

That's because I already know exactly how slow it is. Though I was trying to make it wrap at interactive speeds (20 fps, say) *while* manually resizing. :D I think, as you say, this would be a rare occurence.

Quote:
If you are prepared to put up with certain limitations (eg. losing bookmarks, losing hyperlinks) then you can do it yourself.

I know I can. :)
And I probably would be ok with those limitations in version 1.
USA Global Moderator #11
Quote:
Nitpicking that for the hell of it: reflowing like that would lose the time information of when the original lines came in! That's like the most important line data other than the actual line!

Oh yeah. I guess we'd also need to be able to fake timestamps somehow.

Quote:
I am totally against it, by the by. It would kill the speed

That's ridiculous. It wouldn't have any effect on speed at all except for during the second that it takes to resize the window. You don't need to do anything special when not resizing, so nothing gets slowed down.

Quote:
and second, telnet was never something meant to...
Some games have ASCII maps, others use simply ASCII art to put down tables or other framed information. Once you start reflowing, that stuff starts breaking into total ugliness...
However many, many MUDs assume 80 characters (who lists, scores, maps, even room descriptions)...

Why do you two keep saying that MUDs assume 80 characters? MUDs don't assume 80 characters. They assume AT LEAST 80 characters. Aardwolf channel output very regularly extends beyond your mythical 80 character boundary. Imagine that the user isn't a complete idiot and wants to resize between 90 and 85 characters instead of between 80 and 10.

Quote:
Another issue is the buffer size. Say you had the buffer full (to whatever your limit was, say 10,000 lines). Then you make the borders narrower, so it becomes 12,000 lines. Do 2,000 lines get discarded? What if you wrap back to a wider width? Do they somehow magically come back?

I think that's a negligible concern. Considering that resizing the main output is unlikely to be a common event, I think it's ok for the user to in the absolute worst case lose the very oldest lines in the buffer, which would be no different than if the user had received more lines. I believe this is what the chat capture plugin does.

Quote:
One viable option for MUD plugin developers is to consider actually making everything a miniwindow. That is, omit absolutely everything from output, and funnel it into different streams

I think that's difficult for a number of reasons. The first one I can think of is that you can't easily select arbitrary text in a miniwindow, because miniwindows don't really have text. I suppose it'd be an interesting exercise to add that to the chat capture plugin, though. Hmm...
Selecting text that runs off the screen would be tricky...

Quote:
Then the behaviour of the default output window becomes irrelevant.

An interesting suggestion. Completely replace the main display mechanism with one home-grown in Lua, which probably wouldn't work very well without adding significantly more complicated interaction behaviors for selecting text to the output scripts already available.
Or have the ability to fake some of the metadata in the main output window, which doesn't require most of the extra work that would otherwise be necessary to get up to the same level of functionality that the main window already has.

Note that I'm not actually dismissing that idea. I think it's definitely possible to implement arbitrary text selection in a miniwindow, and when I do (or someone else does), I'd like to add it to the chat capture plugin along with line timestamps, and rewrappable hyperlinks, and bookmarks, and whatever else isn't in there yet. But it isn't in there yet, and if I had to choose at this moment which would be less work to add, I have to go with new methods for faking metadata in the main output.
Amended on Fri 20 Aug 2010 03:35 PM by Fiendish
Netherlands #12
Quote:
That's ridiculous. It wouldn't have any effect on speed at all except for during the second that it takes to resize the window. You don't need to do anything special when not resizing, so nothing gets slowed down.


My point exactly. "MUSHclient sucks. It freezes up for four seconds when I resize my window. Not even [other popular clienth ere] is that slow."

I don't want resizing to reflow my stuff. The speed bump would make it a nightmare, since even when ignoring me 100,000 line buffers, I often size the window smaller temporarily because I don't need the full width I usually do (with mw's being hidden at that point.)
Australia Forum Administrator #13
Fiendish said:

But it isn't in there yet, and if I had to choose at this moment which would be less work to add, I have to go with new methods for faking metadata in the main output.


As with most things, you don't get advantages without some corresponding disadvantages. The case of resizing the miniwindows is perhaps an exception (the disadvantage being the slight increase in executable size, and the time taken to do it).

To address the quoted point first, certainly a new routine could be added (or GetLineInfo modified) to supply extra meta-data. In fact the timestamp of the line is already there.

But reflowing the entire output window - since this was designed in initially to not be allowed certain other decisions followed from it, such as allowing a very large output buffer with very fast access.

As Worstje pointed out, there may be times when you just resize smaller to quickly view something else (maybe a plugin in an editor) and don't want to take a possible lengthy hit as the client "helpfully" reflows everything, and then a second hit as it reflows it back when you resize back to full size for testing.

There would be memory considerations. You suggested doing what can currently be done in Lua, and then "a small routine to copy and paste the history buffer". Well effectively you would need two copies of every line, and someone who is close to the limit of memory might push it into a massive amount of paging to accomplish that.

You would have the issue of things like pre-formatted things like maps and scores looking terrible. Then I would get complaints about that.

For example, on Aardwolf:


score
+-------------------------------------------------------------------------+
|                  Gxxxxxxxx the Barbarian                                |
+--------------------------+-------------------+--------------------------+
| Strength     : [ 35/30 ] | Race : Troll      | Practices    : [    27 ] |
| Intelligence : [ 13/13 ] | Class: Warrior    | Trains       : [    67 ] |
| Wisdom       : [ 11/11 ] | Sub  : Barbarian  | Trivia       : [     0 ] |
| Dexterity    : [ 25/25 ] | Sex  : Male       | Quest points : [    10 ] |
| Constitution : [ 25/25 ] | Level: 20         | Quest time   : [     0 ] |
| Luck         : [ 13/13 ] |                   | Goals done   : [     1 ] |


Resized slightly smaller looks like this:


score
+-------------------------------------------------------------------------+
|                  Gxxxxxxxx the Barbarian                                |
+--------------------------+-------------------+--------------------------+
| Strength     : [ 35/30 ] | Race : Troll      | Practices    : [  
  27 ] |
| Intelligence : [ 13/13 ] | Class: Warrior    | Trains       : [  
  67 ] |
| Wisdom       : [ 11/11 ] | Sub  : Barbarian  | Trivia       : [  
   0 ] |
| Dexterity    : [ 25/25 ] | Sex  : Male       | Quest points : [  
  10 ] |
| Constitution : [ 25/25 ] | Level: 20         | Quest time   : [  
   0 ] |
| Luck         : [ 13/13 ] |                   | Goals done   : [  
   1 ] 


So making it narrower just looks hopeless.

But maybe making it wider lets us fit more descriptions in?

This is at 80 columns:


Academy Courtyard Fountain (G)
  A majestic fountain dominates the center of the academy courtyard. The 
flagstones surrounding the fountain reflect its rainbow hues, and split in 
four directions. The academy rises majestically to the north, while the gates
to the outside world lie directly south. The academy gardens lie both east 
and west, for those who desire a peaceful stroll.
[Exits: north east south west]
     A majestic fountain marks the center of the courtyard.


And now making it wider to fit more per line:


Academy Courtyard Fountain (G)
  A majestic fountain dominates the center of the academy courtyard. The 
flagstones surrounding the fountain reflect its rainbow hues, and split in 
four directions. The academy rises majestically to the north, while the gates
to the outside world lie directly south. The academy gardens lie both east 
and west, for those who desire a peaceful stroll.
[Exits: north east south west]
     A majestic fountain marks the center of the courtyard.


Ah, it looks the same. So why bother? This is my point. People have been happy with the client like this for 15 years - the auto-resizing has not been something I get complaints about.

Fiendish said:

Resisting what? I'm not saying that YOU should do any of this. :)


Who are you addressing your comments to then?
Amended on Fri 20 Aug 2010 09:22 PM by Nick Gammon
USA #14
Windows sends fluid resize events, so you'd actually have to reflow multiple times while the user is still dragging the window borders. If there's a lot of content to reflow, that makes it look extremely laggy and not very enjoyable, and in programs that do this I've tried not to resize if I can avoid it.
Australia Forum Administrator #15
Fiendish said:

I think that's difficult for a number of reasons. The first one I can think of is that you can't easily select arbitrary text in a miniwindow, because miniwindows don't really have text. I suppose it'd be an interesting exercise to add that to the chat capture plugin, though. Hmm...
Selecting text that runs off the screen would be tricky...


WoW has chat windows which are effectively little miniwindows (with a semi-transparent background). There are pretty hopeless, I usually can't even select text at all, let alone part of a line, and they only seem to save about the last 100 lines of chat.

Still, they have 10+ million subscribers, so maybe that isn't a big issue, huh?

Your current chat windows resize nicely, with the amount of stuff I have in them. And with the resize window and move hotspot functionality you can make it faster and the coding simpler.

You could add a RH-click to copy the line under the cursor functionality, that could be nice. And you could add timestamps if you RH-click, or hover over the line, etc.

[EDIT] I take that back, they are already time-stamped.

I honestly think you are trying to solve the wrong problem. Your current chat windows are the direction I would be heading - multiple windows even (eg. clan, newbie, general, auction), and then let the player move them around and keep an eye on them.

Even if your suggestions were implemented there is still only one output window in MUSHclient - that doesn't give you multiple chat windows.
Amended on Fri 20 Aug 2010 09:36 PM by Nick Gammon
USA #16
The original question was specifically related to the "textrectangle" feature. It seems if you resize the font in there and need to resize the window (as a user), you are pretty much all out of luck. Existing text in the window buffer not resizing is no problem, it's a temporary issue. The window resize issue is more of a problem.

Where I'm coming from is providing a downloadable version of the client preconfigured. I have to set the textrectangle value to *something* and the first thing many users are going to want to do is increase or decrease the size of the font in that window to suit their preferences. Sure as a user I'm rarely going to resize the text in that window, but the one time I do (at the beginning) it needs to work.

If it were a normal mini-window I can add increase/decrease font buttons and a drag-handler for window size. If it were a full world window it's a non-issue.

The textrectangle window appears stuck in limbo with no real way for a user to resize it, or to add widgets to it.

I can work around it with stuff to detect the font size and act accordingly of course, or even put a 'config' window over the top of it. There are multiple possible workarounds and you guys probably have better suggestions than these .. the real point of the note was to see if I need to. It's also quite possible there are options for things you can do with 'textrectangle' areas I just haven't discovered yet...
Amended on Fri 20 Aug 2010 10:13 PM by Lasher
USA #17
Lasher said:
The textrectangle window appears stuck in limbo with no real way for a user to resize it, or to add widgets to it.

You could try placing a small, transparent miniwindow just over the bottom-right corner, and make it draggable and stuff. When it's clicked (and held), draw two "guide" lines (with a new miniwindow) to show where the borders will be. When it's released, call TextRectangle() with the new values. (You can do this for other corners too, of course.)

To make it obvious that the corner is draggable, you can draw an image on the dragger, something like the corner graphic Chrome puts on text box form elements.
USA Global Moderator #18
Nick et al.,
I've been addressing Lasher's request for "Auto-wrap to window size" to work on a textrectangle with suggestions of how it might be made to work and reasons why one might want it, presupposing that the desired functionality is for resizing the textrectangle to wrap old lines as well as newly received lines. This could of course have been a foolish assumption on my part. If the desired functionality, as Lasher hints at in his last post, is to only set the wrap point for newly incoming lines based on the width of the textarea, then the problem is very different and much simpler, since the obvious solution is to make the statement "With output redirected to a TextRectangle this setting appears to be ignored" not be true. Twisol's latest suggestion is also more to the point on resizing itself. But I don't think that resizing is always the right answer. The user may want a certain font size and a certain textrectangle size.

If that answers all of your concerns, stop reading this post now. Now on with more blather. :)

Worstje said:

I don't want resizing to reflow my stuff. The speed bump would make it a nightmare, since even when ignoring me 100,000 line buffers, I often size the window smaller temporarily because I don't need the full width I usually do (with mw's being hidden at that point.)

I'm not sure I understand how the scenario you present is possible in the context of textrectangle.

Nick Gammon said:
To address the quoted point first, certainly a new routine could be added (or GetLineInfo modified) to supply extra meta-data. In fact the timestamp of the line is already there.

Nick, to address half of your question of what part you would play in any of this. This is probably it, if anything. All the rest would be handled by someone else who cares to use the functionality in a particular way. To address the second half, see the opening paragraph of this note.

Quote:
There would be memory considerations. You suggested doing what can currently be done in Lua, and then "a small routine to copy and paste the history buffer". Well effectively you would need two copies of every line, and someone who is close to the limit of memory might push it into a massive amount of paging to accomplish that.

That's true of most new software functionality. I'm not really concerned with marginally increased memory requirements.

Quote:
For example, on Aardwolf: ... Ah, it looks the same. So why bother?

Set your width to 100 characters. Watch a few channels. Then set your width to 85 characters. It doesn't look the same. Or are you thinking that with chat capture the way it is, we never need to deal with long lines in the main output area? I don't think I can agree with that premise.

Twisol said:
Windows sends fluid resize events, so you'd actually have to reflow multiple times while the user is still dragging the window borders.

I already encountered this issue with my chat capture plugin. The short answer is "no you don't". The slightly longer answer is, "No you don't. And besides, we're not even talking about Windows resize events. We're talking about a textrectangle. So you're doubly in error."

Nick Gammon said:
You could add a RH-click to copy the line under the cursor functionality, that could be nice. And you could add timestamps if you RH-click, or hover over the line, etc.

[EDIT] I take that back, they are already time-stamped.

It also already lets you click to copy messages. :D
What it doesn't let you do is select an arbitrary block of text. I hope to add that eventually.

Amended on Fri 20 Aug 2010 11:13 PM by Fiendish
Australia Forum Administrator #19
Lasher said:

The original question was specifically related to the "textrectangle" feature. It seems if you resize the font in there and need to resize the window (as a user), you are pretty much all out of luck. Existing text in the window buffer not resizing is no problem, it's a temporary issue. The window resize issue is more of a problem.


Ah well this is totally different. And indeed a small "resize" box in the bottom RH corner of the text rectangle might do what Twisol suggested, let the player generate calls to TextRectangle on the fly.