Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Plugins ➜ Problems with styles and mini windows

Problems with styles and mini windows

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Keberus   (31 posts)  Bio
Date Sat 09 Jan 2010 03:54 PM (UTC)
Message
Okay, I have been able to modify Garrion's ChatWindow plugin to suit my game. It even captures colors from the styles, but because I need it to break up lines, I am having an issue on how to iterate or break up the style. What happens, is as long as the line is shorter than the window, it looks perfect but if it has to go into the part where it needs to be broken up, I don't know what to do with the style to properly break it up, so it doesn't keep sending the whole thing. Here's the code for anyone interested: http://mushclient.pastebin.com/d11c28506. The area that I'm having problems with is this:


  -- capture chats
function chats (name, line, wildcards, styles)

  if #line < wrap_column then
    add_line (styles)  --Works fine
    refresh()
    return
  end -- if

  -- wrap long lines, at a space if possible
  if #line >= 1 then
    while #line > wrap_column do
    
      -- find a space not followed by a space, closest to the end of the line
      local col = string.find (line:sub (1, wrap_column), "%s%S*$")
      
      if col and col > 2 then
        col = col - 1  -- use the space to indent
      else
        col = wrap_column  -- just cut off at wrap_column
      end -- if
 
  --    add_line (line:sub (1, col))
      add_line( styles ) --Keeps sending the full style
      line = line:sub (col + 1, #line)
 
      refresh()
 
    end -- while line > max 
  end -- if

  if #line > 0 then
   add_line (styles)
   refresh()
  end 
end -- chats


Any suggestions on how to handle this properly or in a way that will work would be appreciated.

Thanks a bunch,
KeB
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #1 on Sat 09 Jan 2010 07:56 PM (UTC)
Message
A style run is basically a table of styles, starting at index 1 for the first style. Each entry consists of:


  • "text" - the actual text
  • "length" - how long the text is (same as #text in Lua)
  • "textcolour" - the RGB code for the text colour
  • "backcolour" - the RGB code for the background colour
  • "style" - whether it is bold, underlined etc.


In your loop that handles lines that are too long, you really want to pull out data from styles, not the whole line.

For each style, if it fits, add that style to the list of styles to be displayed on that line. If not, then you will need to find the space (if any) inside that style, that is within your column range. If you can find one then you can split that style into two. Both pieces will have the same colours as the original, however the first will be the text up to the space, and the second, the text after the space.

Then you start a new line, starting with any left-over styles that did not fit on the first line, repeat until you are out of style runs.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Keberus   (31 posts)  Bio
Date Reply #2 on Sun 10 Jan 2010 09:58 PM (UTC)
Message
Nick Gammon said:

For each style, if it fits, add that style to the list of styles to be displayed on that line. If not, then you will need to find the space (if any) inside that style, that is within your column range. If you can find one then you can split that style into two. Both pieces will have the same colours as the original, however the first will be the text up to the space, and the second, the text after the space.


Okay, I've read over this quite a few times, but I'm still a bit unsure what to do with the style, how do you split a style in to, do I need to create a new table, and save all the style information into it, or is there another way to split a style?

Thanks,
KeB
Top

Posted by Twisol   USA  (2,257 posts)  Bio
Date Reply #3 on Sun 10 Jan 2010 10:06 PM (UTC)
Message
You create a new table, yes. In brief (and I didn't look at the code, so you may want to tweak a detail or two): Copy everything from the original to the new table, except set 'text' to whatever the exact text that will fit on that line is, and set 'length' to #text. Then you do the same for the other side of the split, setting 'text' to the rest of the stuff not used and 'length' to its #text again. The second split style is kept handy and used for the next line.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #4 on Sun 10 Jan 2010 11:09 PM (UTC)

Amended on Sun 31 Jan 2010 06:58 PM (UTC) by Nick Gammon

Message
Well it is quite tricky to get perfect. This demo plugin should help. It simply catches all lines from the MUD and copies them to a miniwindow, keeping colours, and wrapping if the lines won't fit.

Template:saveplugin=Chat_Window_Demo To save and install the Chat_Window_Demo plugin do this:
  1. Copy between the lines below (to the Clipboard)
  2. Open a text editor (such as Notepad) and paste the plugin into it
  3. Save to disk on your PC, preferably in your plugins directory, as Chat_Window_Demo.xml
  4. Go to the MUSHclient File menu -> Plugins
  5. Click "Add"
  6. Choose the file Chat_Window_Demo.xml (which you just saved in step 3) as a plugin
  7. Click "Close"



<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="Chat_Window_Demo"
   author="Nick Gammon"
   id="93068d80a35c123ed55376dc"
   language="Lua"
   purpose="Redirects all output to a miniwindow"
   date_written="2010-01-11 10:04"
   requires="4.37"
   version="2.0"
   >

</plugin>

<triggers>
  <trigger
   enabled="y"
   match="*"
   script="chat_handler"
   sequence="100"
  >
  </trigger>
</triggers>


<!--  Script  -->


<script>
<![CDATA[

require "copytable"

-- configuration

-- window size in pixels
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 800 

MAX_LINES = 1000 -- how many lines to store

-- font
FONT_NAME = "Dina"
FONT_SIZE = 8

-- where to put the window
WINDOW_POSITION = 6  

-- colours
WINDOW_BACKGROUND_COLOUR = 0x111111

-- offset of text from edge
TEXT_INSET = 5

-- where to store the chat line
lines = {}  -- table of recent chat lines

-- display one line
function Display_Line (line, styles)

 
  local left = TEXT_INSET
  local top = (line - 1) * font_height
  
  for _, v in ipairs (styles) do
    left = left + WindowText (win, "f", v.text, left, top, 0, 0, v.textcolour)
  end -- for each style run                 

 
end -- Display_Line

-- here on chat line
function chat_handler (name, line, wildcards, styles)

  -- blank existing window contents
  WindowRectOp (win, 2, 0, 0, 0, 0, WINDOW_BACKGROUND_COLOUR)
  WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15)
  
  local avail = 0
  local line_styles
  
  -- blank line? force one entry
  if #styles == 0 then
    -- remove first line if filled up
    if #lines >= MAX_LINES then
      table.remove (lines, 1)
    end -- if 
    table.insert (lines, {})
  end -- if
  
  -- keep pulling out styles and trying to fit them on the current line
  
  while #styles > 0 do
  
    -- no room available? start new line
    
    if avail <= 0 then
      -- remove first line if filled up
      if #lines >= MAX_LINES then
        table.remove (lines, 1)
      end -- if 
      avail = WINDOW_WIDTH - (TEXT_INSET * 2)
      line_styles = {}
      table.insert (lines, line_styles)
    end -- line full

    -- get first style, work out how long it is
    local style = table.remove (styles, 1)  -- pull out first style
    local width = WindowTextWidth (win, "f", style.text)  -- how long this style is

    -- if it fits, copy whole style in
    if width <= avail then
      table.insert (line_styles, style)
      avail = avail - width
    else -- otherwise, have to split style
    
      -- look for trailing space (work backwards)
      -- remember where space is
      
      local col = style.length - 1
      local split_col
      
      -- keep going until out of columns
      
      while col > 1 do
        width = WindowTextWidth (win, "f", style.text:sub (1, col))
        if width <= avail then
          if not split_col then
            split_col = col  -- in case no space found, this is where we can split
          end -- if
          
          -- see if space here
          if style.text:sub (col, col) == " " then
            split_col = col
            break
          end -- if space
        end -- if will now fit
        col = col - 1
      end -- while
          
      -- if we found a place to split, use old style, and make it shorter
      -- also make a copy and put the rest in that
      if split_col then
        table.insert (line_styles, style)
        local style_copy = copytable.shallow (style)
        style.text = style.text:sub (1, split_col)
        style.length = split_col 
        style_copy.text = style_copy.text:sub (split_col + 1)
        style_copy.length = #style_copy.text
        table.insert (styles, 1, style_copy)
      else
        if next (line_styles) == nil then
          table.insert (line_styles, style)
        else
          table.insert (styles, 1, style)
        end -- if
      end -- if    
    
      avail = 0  -- now we need to wrap
      
    end -- if could not fit whole thing in
          
  end -- while we still have styles over
  

  -- display all lines
  
  local first_visible = #lines - visible_lines
  if first_visible < 1 then
    first_visible = 1
  end -- if
  
  local count = 0
  for i = first_visible, #lines do
    count = count + 1
    Display_Line (count, lines [i])
  end -- for
  
  -- force window redisplay
  WindowShow (win,  true)  -- show it 
               
end -- end chat_handler

-- startup stuff

win = GetUniqueID ()  -- get a unique name

-- make the window
WindowCreate (win, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_POSITION, 0, 
                         WINDOW_BACKGROUND_COLOUR)  -- create window
               
-- grab a font
WindowFont (win, "f", FONT_NAME, FONT_SIZE) -- define font

-- work out how high it is
font_height = WindowFontInfo (win, "f", 1)   -- height of the font  

-- work out how many lines will fit
visible_lines = math.floor (WINDOW_HEIGHT / font_height) - 1

]]>
</script>
</muclient>


Basically when a line arrives, we have to gradually add the styles from that input line, into the saved line for the miniwindow, stopping when it won't fit. However if it won't fit, we need to examine the last style (the one that won't fit totally) and look backwards for a space, that happens to be inside the "fitting" area.

It looks messy, maybe someone can do a better job.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Keberus   (31 posts)  Bio
Date Reply #5 on Tue 12 Jan 2010 01:08 AM (UTC)

Amended on Tue 12 Jan 2010 01:33 AM (UTC) by Keberus

Message
Many, many thanks for the help Nick, I now have the MW working, exactly how I wanted. Here's the final product for anyone who might be interested.
http://mushclient.pastebin.com/f400d972f

I renamed the plugin, changed the id, and gave credit to Nick and Garrion for their help.

Thanks again,
KeB
Top

Posted by Nick Gammon   Australia  (23,133 posts)  Bio   Forum Administrator
Date Reply #6 on Sun 31 Jan 2010 07:02 PM (UTC)

Amended on Sun 31 Jan 2010 07:03 PM (UTC) by Nick Gammon

Message
To keep the background colours as well, change:




-- display one line
function Display_Line (line, styles)

 
  local left = TEXT_INSET
  local top = (line - 1) * font_height
  
  for _, v in ipairs (styles) do
    left = left + WindowText (win, "f", v.text, left, top, 0, 0, v.textcolour)
  end -- for each style run                 

 
end -- Display_Line


to read:


-- display one line
function Display_Line (line, styles)
 
  local left = TEXT_INSET
  local top = (line - 1) * font_height

  for _, style in ipairs (styles) do
    local width = WindowTextWidth (win, "f", style.text) -- get width of text
    local right = left + width
    local bottom = top + font_height
    WindowRectOp (win, 2, left, top, right, bottom, style.backcolour)  -- draw background
    WindowText (win, "f", style.text, left, top, right, bottom, style.textcolour)  -- draw text
    left = left + width  -- advance horizontally
  end -- for each style run        

end -- Display_Line


This draws coloured rectangles, the same colour as the style background colour, before each style of text.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


19,815 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.