Text with different colored letters

BlitzMax Forums/BlitzMax Programming/Text with different colored letters

Tibit(Posted 2010) [#1]
It can be a bit trixy to insert color in the middle of text.

One approach I know of is to use some special sign that you don't expect anyone to use, such as # and then you mod the draw method to parse for these signs and set the color respectively.

Ex:
Color.Black.Apply
tooltip:String = This #green spell# does [#red 50 #] damage.

It definitely reduces the readability of the text, it is especially bad for localization purposes, since someone doing localization can missplace a # somewhere, in some languages the structure of the sentence can be very different and coloring should take that in consideration.

I'm still experimenting with a good approach, suggestions welcome! :)

Maybe should add that atm I'm mainly using normal drawtext, but I'm going to use bitmap fonts later on through Ziggy's new fontmachine.

Last edited 2010


_Skully(Posted 2010) [#2]
Obviously if you are going for styles it would be more complex, but if you are just looking at color switching then you could go with something like:


DText.addcolor("red",255,10,10)
DText.addcolor("green",10,255,10)
DText.Draw("This is default black <red>This is RED and this is <green>green",0,0)

only <>'s that match with a set color actually switch the color so <blue> would be printed.

just a concept...


Tibit(Posted 2010) [#3]
using <> does look better, another option is {} hmm


_JIM(Posted 2010) [#4]
If you only define those things in code, you could use something shorter yet more flexible. Like a group of bytes. One to define the color change (like ascii 250) then 3 to define the RGB.

What I currently use is something like this:

Local txt$ = "Click " + TextColor(255,0,0) + "here" + TextColor(255,255,255) + " to do absolutely nothing!"

Not particularly pretty, but highly flexible.


Shortwind(Posted 2010) [#5]
I've done some preliminary, albeit beginnerish, work with this concept.

In the code I've provided, you can see that you could easily go with multiple escape sequences for processing the text. Not only is color available, but you could also include changing fonts, or font attributes (italic), etc.

As you'll also note, you are not limited to a "specific" set of escape characters, nor are your escape sequences limited in length. These can be changed willy-nilly depending on your preference or mood at the time. Adding more codes, and even fully functioning "subroutines or procedures" is possible with this concept.

Forgive the crappyness of the code, but hope this helps your more superior ideas come to light! :D

(Credit must be given to Jesse who helped in the debugging and various ideas.)

Strict
Graphics 1024,768,0,60

Local mywindow:windowbox=WindowBox.Create()

mywindow.setwindowfont("c:\windows\fonts\consola.ttf",22)
'mywindow.setwindowfont("c:\windows\fonts\arial.ttf",22)
mywindow.setpos(150,100)
mywindow.setsize(30,10)
'mywindow.settexture("text_texture.png")

Cls
SetColor 255,255,255
SetBlend AlphaBlend

mywindow.show()
mywindow.CurX=0
mywindow.CurY=0
mywindow.printwrap("{Y}Hello.~n~n")
mywindow.printwrap("{W}This is a {Red}test {W}of something.")
mywindow.printwrap("And more testing is in order.")
mywindow.printwrap("Because the {Y}National {W}concensis is that I'm more {Red}off {W}than more on.")
mywindow.printwrap("Even more {Red}unbelievable {W}is that this thing might be working.")
Flip

Repeat
Until KeyDown(KEY_ESCAPE) Or AppTerminate()

Type WindowBox
	'Global WindowBoxList:TList = New TList
	Global WinCodes:Codes=New Codes.Create()
		
	Field Font_Name:String
	Field Font_Size:Int
	Field FontMaxWidth:Int
	Field FontMaxHeight:Int
	Field ScreenWidth:Int
	Field ScreenHeight:Int
	Field WindowXPos:Int
	Field WindowYPos:Int
	Field WindowWidth:Int
	Field WindowHeight:Int
	Field TexturePath:String
	Field TextureImage:TImage = New TImage
	Field CurX:Int
	Field CurY:Int
	Field TextLines:TextLine[100]
	Field CurTextLine:Int
	Field CurTopLine:Int
	Field CurRed:Int
	Field CurGreen:Int
	Field CurBlue:Int
	
	Function Create:WindowBox()
		Local w:WindowBox = New WindowBox
		w.SetWindowFont(Null,8)
		w.ScreenWidth=GraphicsWidth()
		w.ScreenHeight=GraphicsHeight()
		w.SetPos(100,100)
		w.SetSize(10,10)
		w.CurX=0
		w.CurY=0
		w.CurRed=255
		w.CurGreen=255
		w.CurBlue=255
		w.CurTextLine=0
		w.CurTopLine=0
		w.TextLines[0]=New TextLine.Create()
'		WindowBoxList.addlast(w:WindowBox)
		Return w:WindowBox
	End Function

	Method SetWindowFont(Font_Namex:String,Font_Sizex:Int)
		Local font:timagefont=LoadImageFont(font_namex,font_sizex,SMOOTHFONT)
		SetImageFont(font)
		Font_Name=Font_Namex
		Font_Size=Font_Sizex
		FontMaxWidth=TextWidth("z")
		FontMaxHeight=TextHeight("z")
		Rem
		FontMaxWidth=TextWidth(Chr(0))
		FontMaxHeight=TextHeight(Chr(0))
		For Local i:Int=65 To 91
			If TextWidth(Chr(i))>FontMaxWidth Then FontMaxWidth=TextWidth(Chr(i))
			If TextHeight(Chr(i))>FontMaxHeight Then FontMaxHeight=TextWidth(Chr(i))
		Next
		End Rem
	End Method
	
	Method SetPos(xpos:Int, ypos:Int)
		WindowXPos=xpos
		windowYPos=ypos
	End Method
	
	Method SetSize(xsize:Int, ysize:Int)
		WindowWidth=xsize
		WindowHeight=ysize
	End Method
	
	Method DrawBorder()
		SetColor 255,255,255
		Local x:Int=WindowXPos
		Local x1:Int=WindowXPos+(WindowWidth*FontMaxWidth)+FontMaxWidth
		Local y:Int=WindowYPos
		Local y1:Int=WindowYPos+(WindowHeight*FontMaxHeight)
		
		DrawLine(x-5,y-5,x1+5,y-5)
		DrawLine(x-5,y-5,x-5,y1+5)
		DrawLine(x1+5,y-5,x1+5,y1+5)
		DrawLine(x-5,y1+5,x1+5,y1+5)
	End Method
			
	Method Show()
		Local x1:Int=(WindowWidth*(FontMaxWidth))
		Local y1:Int=(WindowHeight*(FontMaxHeight))
		DrawBorder()
		'DrawImageRect(TextureImage,WindowXPos,WindowYPos,x1,y1)
	End Method
	
	Method SetTexture(TexturePathx:String)
		TextureImage = LoadImage(TexturePathx)
	End Method

	Method SetDefaultColor(Redx:Int,Greenx:Int,Bluex:Int)
		CurRed=Redx
		CurGreen=Greenx
		CurBlue=Bluex
	End Method
	
	Method PrintWrap(Text:String)
		Local words:String[]
		Local Ret:String[]
		Local soffx:Int=WindowXPos
		Local soffy:Int=WindowYPos
		Local codex:String
		Local wordx:String
		Local kcodevalue:Int
		
		words=Text.split(Chr(32))
		
		For Local t:String=EachIn words
			Ret=t.split(Chr(10))
			t=Left(t,Len(t)-(Len(ret)-1))
			If Left(t,1)="{" Then
				Local y:String[]
				y=t.split("{")
				For Local j:String=EachIn y
					Local tt:Int=Instr(j,"}")
					codex=Left(j,tt-1)
					wordx=Mid(j,tt+1)
					kcodevalue=WinCodes.GetCode(codex)
				Next
			Else
				wordx=t
				codex="None"
				kcodevalue=0
			EndIf
			If wordx.length<WindowWidth-CurX Then
				If CurX=0 Then CurY=CurY+1
					TextLines[CurTextLine].AddWord(wordx,wordx.length,codex,kcodevalue)
				Else
					CurX=0
					CurY=CurY+1
					If CurY > WindowHeight Then
						CurTopLine=CurTopLine+1
					EndIf
					CurTextLine=CurTextLine+1
					TextLines[CurTextLine]=New TextLine.Create()
					TextLines[CurTextLine].AddWord(wordx,wordx.length,codex,kcodevalue)
				End If
				CurX=CurX+wordx.length+1
			
				If CurX > WindowWidth Then
					CurX=0
					CurY=CurY+1
					If CurY > WindowHeight Then
						CurTopLine=CurTopLine+1
					EndIf
					CurTextLine=CurTextLine+1
				EndIf			
			
				If Len(Ret)>1 Then
					For Local i:Int=1 To Len(ret)-1
						CurX=0
						CurY=CurY+1
						If CurY > WindowHeight Then
							CurTopLine=CurTopLine+1
						EndIf
						CurTextLine=CurTextLine+1
						TextLines[CurTextLine]=New TextLine.Create()
						TextLines[CurTextLine].AddWord("~n",-1,"~n",10)
					Next
				EndIf
		Next
		
		SetColor 0,0,0
		DrawRect(WindowXPos,WindowYPos,WindowWidth*FontMaxWidth,(WindowHeight)*FontMaxHeight)
		SetColor CurRed,CurGreen,CurBlue
		Local i:Int=CurTopLine
		Local xpos:Int
		Local ypos:Int
		While (TextLines[i]<>Null) And (i<=(WindowHeight+CurTopLine-1))
			xpos=0
			ypos=(i-CurTopLine)*FontMaxHeight
			For Local j:Words=EachIn TextLines[i].WordList
				If j.thecodevalue<>0 Then
					WinCodes.codefunc[j.thecodevalue]
				EndIf
				DrawText(j.theword,soffx+xpos,soffy+ypos)
				xpos=xpos+((j.thelength+1)*FontMaxWidth)

			Next
		i=i+1
		Wend
	End Method
	 
End Type

Type Words
	Field theword:String
	Field thecode:String
	Field thecodevalue:Int
	Field thelength:Int
	
	Function Create:Words(thewordx:String,thelengthx:Int,thecodex:String,thecodevaluex:Int)
		Local w:Words=New Words
		w.theword=thewordx
		w.thelength=thelengthx
		w.thecode=thecodex
		w.thecodevalue=thecodevaluex
		Return w
	End Function
End Type

Type TextLine
	Field wordlist:TList=CreateList()

	Function Create:TextLine()
		Local w:Textline=New Textline
		Return w
	End Function
	
	Method AddWord(w:String,l:Int,c:String,v:Int)
			ListAddLast(wordlist,words.Create(w,l,c,v))
	End Method

End Type

Type Codes
	Field codefunc()[]
	
	Function Create:Codes()
		Local w:codes=New codes
		w.codefunc=w.codefunc[..255]
		w.codefunc[0]=w.f0
		w.codefunc[1]=w.f1
		w.codefunc[2]=w.f2
		w.codefunc[3]=w.f3
		w.codefunc[4]=w.f4
		w.codefunc[10]=w.f10
		w.codefunc[32]=w.f32
		Return w:Codes
	End Function
	
	Function f0()
		'Print "None"
	End Function
	
	Function f1()
		'Black
		SetColor 0,0,0
	End Function
	
	Function f2()
		'White
		SetColor 255,255,255
	End Function
	
	Function f3()
		'Yellow
		SetColor 255,255,0
	End Function
	
	Function f4()
		'Red
		SetColor 255,0,0
	End Function
	
	Function f10()
		'Print "chr(10)"
	End Function
	
	Function f32()
		'Print "Space"
	End Function
	
	Method GetCode:Int(x:String)
		Select x
		Case "B"; Return 1
		Case "W"; Return 2
		Case "Y"; Return 3
		Case "Red"; Return 4
		Case "~n"; Return 10
		End Select	
	End Method
End Type



Last edited 2010


Tibit(Posted 2010) [#6]
Nice!

I tried to modify it to work in realtime. However textwrap seems to both add text and draw it?

I was thinking Pixmap. One could draw the text box with colors and everything once to a pixmap, and then draw that pixmap so that we do not have to recalculate colors all the time. Problem is of course if we want to draw a infobox with data in that change in real-time.