Thread: Autowrapping text

Page 1 of 2 12 LastLast
Results 1 to 10 of 17
  1. #1 Autowrapping text 
    Registered Member

    Join Date
    Sep 2016
    Posts
    181
    Thanks given
    84
    Thanks received
    74
    Rep Power
    452
    No more line1, line2, ..., line5 bull shit.

    What you will get
    "Be able to display normal text \\n" +
    "without manually having to make \\n" +
    "line breaks in the client. Also \\n" +
    "includes dynamic text interface type \\n" +
    "which automatically wraps to fit \\n" +
    "the given width."

    Part 1

    Core method is this. Add it to RSInterface.java or any suitable place.
    Code:
    /**
     * Adds newlines to a text for a certain TextDrawingArea so each line is never longer than width.
     * Param tda The textDrawing Area for the text, basically the font
     * Param text The text to convert to wrapped text
     * Param width The width above which wrapping is applied
     * Return The wrapped text
     */
    public static String getWrappedText(TextDrawingArea tda, String text, int width) {
    	if (text.contains("\\n") || tda.getTextWidth(text) <= width) {
    		return text;
    	}
    	int spaceWidth = tda.getTextWidth(" ");
    	StringBuilder result = new StringBuilder(text.length());
    	StringBuilder line = new StringBuilder();
    	int lineLength = 0;
    	int curIndex = 0;
    	while (true) {
    		int spaceIndex = text.indexOf(' ', curIndex);
    		int newLength = lineLength;
    		boolean last = false;
    		String curWord;
    		if (spaceIndex < 0) {
    			last = true;
    			curWord = text.substring(curIndex);
    		} else {
    			curWord = text.substring(curIndex, spaceIndex);
    			newLength += spaceWidth;
    		}
    		curIndex = spaceIndex + 1;
    		int w = tda.getTextWidth(curWord);
    		newLength += w;
    		if (newLength > width) {
    			result.append(line);
    			result.append("\\n");
    			line = new StringBuilder(curWord);
    			line.append(' ');
    			lineLength = w;
    		} else {
    			line.append(curWord);
    			line.append(' ');
    			lineLength = newLength;
    		}
    		if (last) {
    			break;
    		}
    	}
    	result.append(line);
    	return result.toString();
    }
    Somebody might get this cleaner but eh it works perfectly atm.

    This goes over the text for a given font and adds line breaks in spaces to prevent any line exceeding the specified width.
    Precondition is the text doesn't contain the client's custom new line string, \\n, since it uses that to check if wrapping has already been applied.

    You can call this method now to get wrapped text for a specified width which is useful as is, but we can also apply it dynamically.

    Part 2
    New wrappable text interface type. Copy an existing addText method and change the type to 17 instead of 4 (can use constants, surprised I didn't). For the message call the method from the first part to getWrappedText. Make an apropriate method name for the new method.
    Code:
    	/**
    	 * Adds text with the specified properties. Automatically wraps text so it doesn't exceed width.
    	 * Only use for dynamic interfaces as there is some computation to check if wrapping is required.
    	 * If static text, use another addText method and pass the text into RSInterface.getWrappedText() firstly.
    	 *    Param id The child id for the text
    	 *    Param text The text message
    	 *    Param tda The tdas available
    	 *    Param idx The index of the tda to use
    	 *    Param color The text color
    	 *    Param center Whether the text is centered
    	 *    Param shadow Whether the text has shadow
    	 *    Param width The maximum width of each line before wrapping applies
    	 *    Return
    	 */
    	public static RSInterface addWrappingText(int id, String text, TextDrawingArea tda[],
    									  int idx, int color, boolean center, boolean shadow, int width) {
    		RSInterface tab = addTabInterface(id);
    		tab.parentID = id;
    		tab.id = id;
    		tab.type = 17;
    		tab.atActionType = 0;
    		tab.width = width;
    		tab.height = 11;
    		tab.contentType = 0;
    		tab.opacity = 0;
    		tab.hoverType = -1;
    		tab.centerText = center;
    		tab.shadowed = shadow;
    		tab.textDrawingAreas = tda[idx];
    		tab.message = getWrappedText(tab.textDrawingAreas, text, tab.width);
    		tab.enabledMessage = "";
    		tab.disabledColor = color;
    		tab.enabledColor = 0;
    		tab.disabledMouseOverColor = 0;
    		tab.enabledMouseOverColor = 0;
    		return tab;
    	}
    Will post proper code later, this is from memory now.
    Go to Client.java. For all uses of interface type == 4 add || interface type == 17.
    For the part that loads the string from the interface replace
    Code:
    String s = rsi.message
    with
    Code:
    String s;
    if (rsi.type == 17) {
    	s = RSInterface.getWrappedText(rsi.tda, rsi.message, rsi.width);
    } else {
    	s = rsi.message;
    }
    You should be done! Double check you have checked all the type 4 cases and added the wrappable text type in as well otherwise you might have nothing displaying or errors. I'll post some media later or you can test it on LoyaltyScape with ::t to open the beta teleport interface and check the minigames category which has nice autowrapping descriptions with a width of 126 pixels.

    Edit: Media
    Attached image

    I asked if I should release this and Displee said I should so here you go. This is for 317s bit could easily be adapted to any client. I was making a teleport interface and had a tight space to fit any text information and other interfaces in the client done by other people would do separate lines and then have to manually decide which text goes on which line which is ridiculous. E.g.
    Code:
    private BossData(String name, int npcId, int npcZoom, int[][] bossItems, String line1, String line2, String line3, String line4, String line5) {
    			this.name = name;
    			this.npcId = npcId;
    			this.npcZoom = npcZoom;
    			this.bossItems = bossItems;
    			this.line1 = line1;
    			this.line2 = line2;
    			this.line3 = line3;
    			this.line4 = line4;
    			this.line5 = line5;
    
    		}
    This also has advantage of having the text in a single child id. Additionally there is no cap for a line limit.
    Note I didn't support color modifiers such as @red@. Not a fan of them and you can add that yourself.
    Last edited by The Goldfish; 12-26-2019 at 07:10 PM. Reason: Improved code using StringBuilder, consistent {}, and a small bug, added pic
    Reply With Quote  
     

  2. #2  
    Registered Member
    Join Date
    Dec 2019
    Posts
    23
    Thanks given
    5
    Thanks received
    3
    Rep Power
    11
    Thanks this could be nice for Multiple Languages since some words are longer then others so once u write in English and translate will just wrap for you
    Reply With Quote  
     

  3. #3  
    Registered Member

    Join Date
    Sep 2016
    Posts
    181
    Thanks given
    84
    Thanks received
    74
    Rep Power
    452
    Quote Originally Posted by RuneShark_ View Post
    Thanks this could be nice for Multiple Languages since some words are longer then others so once u write in English and translate will just wrap for you
    Yeah that would be a good example. At the moment I use it for dynamc interfaces where te server updates the text when the user loads different teleports.
    Reply With Quote  
     

  4. #4  
    Software Developer

    Tyrant's Avatar
    Join Date
    Jul 2013
    Age
    24
    Posts
    1,562
    Thanks given
    678
    Thanks received
    423
    Rep Power
    1060
    Could do it even better letting the system know when text is out of bounds and thus going down a line.

    It's better since in many occasions you wouldn't know how long the line is to put a new line mark
    Reply With Quote  
     

  5. #5  
    Registered Member

    Join Date
    Dec 2009
    Posts
    774
    Thanks given
    367
    Thanks received
    455
    Rep Power
    927
    Good job.
    link removed
    Reply With Quote  
     

  6. #6  
    Renown Programmer
    Greg's Avatar
    Join Date
    Jun 2010
    Posts
    1,179
    Thanks given
    260
    Thanks received
    1,012
    Rep Power
    2003
    You should look into using StringBuilder for concatenating strings because Strings are immutable so every time you join two you create a third. String builder doesn't do this until the joining is complete.

    You could also split it automatically using commons-text WordUtils

    Your code would then look like
    Code:
    String.join("\n", WordUtils.wrap(string, 45))
    . Kotlin users have the wrap function natively.
    This applies for both server and 317 client-side.
    Attached imageAttached image
    Reply With Quote  
     

  7. Thankful users:


  8. #7  
    nice


    Join Date
    Jul 2014
    Posts
    740
    Thanks given
    382
    Thanks received
    562
    Rep Power
    4239
    good job, thought this could be simplified
    also why are you so inconsistant with skipping / using brackets, example:

    Code:
     
    if (text.contains("\\n") || tda.getTextWidth(text) <= width)
    			return text;
    if (last) {
    				break;
    			}
    Reply With Quote  
     

  9. #8  
    Respected Member


    Join Date
    Jan 2009
    Posts
    5,743
    Thanks given
    1,162
    Thanks received
    3,603
    Rep Power
    5000
    Quote Originally Posted by Suic View Post
    good job, thought this could be simplified
    also why are you so inconsistant with skipping / using brackets, example:

    Code:
     
    if (text.contains("\\n") || tda.getTextWidth(text) <= width)
                return text;
    if (last) {
                    break;
                }
    cause its been copy and pasted from somewhere else then modified
    Reply With Quote  
     

  10. Thankful user:


  11. #9  
    Registered Member

    Join Date
    Sep 2016
    Posts
    181
    Thanks given
    84
    Thanks received
    74
    Rep Power
    452
    Quote Originally Posted by Spooky View Post
    cause its been copy and pasted from somewhere else then modified
    No it was all my code actually lol. I don't know, I used to ALWAYS make my one liner if else statements without brackets, trying to get into the habit of always using {} now since I think it's better incase you come back to modify something at a later date and the braces are already there rather than risk forgetting and causing a bug by adding to the statement and it being executed outside the if else statement.

    Quote Originally Posted by Greg View Post
    You should look into using StringBuilder for concatenating strings because Strings are immutable so every time you join two you create a third. String builder doesn't do this until the joining is complete.

    You could also split it automatically using commons-text WordUtils

    Your code would then look like
    Code:
    String.join("\n", WordUtils.wrap(string, 45))
    . Kotlin users have the wrap function natively.
    This applies for both server and 317 client-side.
    Code:
    public static String getWrappedText(TextDrawingArea tda, String text, int width) {
    	if (text.contains("\\n") || tda.getTextWidth(text) <= width) {
    		return text;
    	}
    	int spaceWidth = tda.getTextWidth(" ");
    	StringBuilder result = new StringBuilder(text.length());
    	StringBuilder line = new StringBuilder();
    	int lineLength = 0;
    	int curIndex = 0;
    	while (true) {
    		int spaceIndex = text.indexOf(' ', curIndex);
    		int newLength = lineLength;
    		boolean last = false;
    		String curWord;
    		if (spaceIndex < 0) {
    			last = true;
    			curWord = text.substring(curIndex);
    		} else {
    			curWord = text.substring(curIndex, spaceIndex);
    			newLength += spaceWidth;
    		}
    		curIndex = spaceIndex + 1;
    		int w = tda.getTextWidth(curWord);
    		newLength += w;
    		if (newLength > width) {
    			result.append(line);
    			result.append("\\n");
    			line = new StringBuilder(curWord);
    			line.append(' ');
    			lineLength = w;
    		} else {
    			line.append(curWord);
    			line.append(' ');
    			lineLength = newLength;
    		}
    		if (last) {
    			break;
    		}
    	}
    	result.append(line);
    	return result.toString();
    }
    So something like this for StringBuilder?
    [Also fixed a bug where it used included the width for a space at the very end].

    For that wrapping function, does that take into account the pixel width of the font though? Or is that a wrap that considers each character with a length of 1? I don't think that's the same thing.

    http://commons.apache.org/proper/com...ng.String-int-

    Yeah this isn't what the snippet is about. 20 '0's is a lot longer than 20 "i"s, but with the WordUtils.wrap method it would wrap them at the same point.

    Attached image
    Reply With Quote  
     

  12. #10  
    Renown Programmer
    Greg's Avatar
    Join Date
    Jun 2010
    Posts
    1,179
    Thanks given
    260
    Thanks received
    1,012
    Rep Power
    2003
    Quote Originally Posted by knd6060 View Post
    So something like this for StringBuilder?
    Yeah looks better. Depending on how many interfaces it's used, just that simple change could save your client 10's MBs of ram overhead.


    Quote Originally Posted by knd6060 View Post
    For that wrapping function, does that take into account the pixel width of the font though? Or is that a wrap that considers each character with a length of 1? I don't think that's the same thing.
    You're correct it splits by characters, not pixels. Splitting by character is more for text sent to client interfaces e.g. dialogues.
    Attached imageAttached image
    Reply With Quote  
     

  13. Thankful user:


Page 1 of 2 12 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Textured Text
    By Looted in forum Tutorials
    Replies: 1
    Last Post: 10-05-2007, 10:02 AM
  2. [TUT] How to make a bloody text! [TUT]
    By Looted in forum Tutorials
    Replies: 11
    Last Post: 09-30-2007, 02:11 AM
  3. Loading Menus From Text File
    By ThatOneServer in forum Tutorials
    Replies: 3
    Last Post: 06-19-2007, 11:01 AM
  4. Loading Commands From Text File
    By ThatOneServer in forum Tutorials
    Replies: 2
    Last Post: 05-05-2007, 12:36 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •