Quote: What I'm at now is trying to figure out the resizing aspect/scrolling that you have in this plugin. Could someone please detail how this script does that, thuroughly.
Math. Also programming. :\
*sigh* Ok, here goes...
As with just about any aspect of user interaction, the place to start looking is in functions that define what happens when the user activates the mouse button while over a hotspot. These are associated with the various hotspots in the calls to WindowAddHotspot and WindowDragHandler (see MUSHclient miniwindow docs). By inspection of the plugin code we can see that they are named MouseDown, ResizeMoveCallback, and to lesser degrees MouseUp, CancelMouseDown, and ResizeReleaseCallback.
Quote: scrolling
Scrolling the text requires that you think of the chat window as an actual narrow window onto a much larger body of text with sequentially numbered lines. At any moment, the window view displays a set of lines of text starting from some line number (let's call it TOP_LINE) and ending at TOP_LINE+N, where N is the number of lines that fit in the window given its current size. When you scroll up and down, all you are doing is changing the value of TOP_LINE. Either subtracting from it when scrolling up, or adding to it when scrolling down. Obviously special care must be taken to not scroll off the ends (you can't show line number -1, for example).
Once you have basic scrolling, the next part is constructing a proportionally sized and spaced scrollbar widget.
The principles of a proportional scrollbar are as follows:
Starting with a chat storage buffer such that each array index contains a single printable line of text (this means chats in the buffer must already be line-wrapped for display. See function fillBuffer that breaks up styleruns according to the miniwindow width), you need to first find out how large to make the shuttle and then where to position it on the scrollbar...
You have a number of buffered text lines (Let's call this number of lines T). You also have a window that can display some number of lines at a time (let's call this number of lines D and is the text display area height divided by the text line height). The third number we'll need is the number of vertical pixels that you want the scrollbar region to fill (let's call that number of pixels P). This could be the entire vertical height of the miniwindow or some subset thereof (accounting for titlebar, resize widget, etc). The ratio of lines that can be displayed to total lines (D/T) governs both the size of the scrollbar shuttle and which set of lines gets displayed for a given bar location.
The height of the scroll shuttle is always going to be math.max(MIN_SHUTTLE_HEIGHT, math.min(P, (D/T)*P)). This means use a proportional number of the available pixels except for two cases. You want a minimum scroll shuttle height so that the shuttle doesn't get too small to grab with the cursor if you have thousands of buffered lines and a very small window. You also want to make sure the shuttle doesn't try to be larger than the designated scrollbar area if you have fewer buffered lines than will fit in the miniwindow (D/T would then be > 1).
The position of shuttle in the scrollbar should be determined in the following way. Divide the scrollbar into a proportional series of pixel increments, one step for each possible line of text to be shown, by (P/T). Now whatever line of text you want to start (or end, depending on orientation preference) the display section with can be directly related to a shuttle position and vice versa.
Quote: the resizing
I'm actually going to explain how it *should have* been done, because in hindsight I have always made resizing functions a bit longer than they need to be. Also the implementation posted in this thread is buggy. Discovering how is left as an exercise for the reader after reading the next paragraph.
On MouseDown save the x and y distances from the mouse's position to the bottom right corner of the miniwindow (width-WindowInfo(win, 17) and height-WindowInfo(win, 18)). Call them offset_x and offset_y. Then, every ResizeMoveCallback, set the window width and height to the current mouse position relative to the miniwindow (WindowInfo(win, 17) and WindowInfo(win,18) again) plus the original offsets, bounded by some arbitrary minimum width/height and some maximum width/height equal to reaching the edges of the screen.
The method implemented at the beginning of this thread is quite similar in nature. But I think that storing and using the original corner offset is probably a philosophically better idea than tracking the mouse movement at every drag step, but they can be equally functional if both done correctly.
After the window has been resized, we want to re-wrap all the old chats so that it all fits nicely into the new width. For performance reasons we can only safely do this when the user releases the mouse button after dragging (ResizeReleaseCallback), because the mechanism for linewrapping the stored chats is, sadly, not nearly fast enough to run dozens of times per second while dragging the resizer.
[EDIT] made a few edits for organizational clarity |