Gamestudio Links
Zorro Links
Newest Posts
Zorro 2.70
by jcl. 09/29/25 09:24
optimize global parameters SOLVED
by dBc. 09/27/25 17:07
ZorroGPT
by TipmyPip. 09/27/25 10:05
assetHistory one candle shift
by jcl. 09/21/25 11:36
Plugins update
by Grant. 09/17/25 16:28
AUM Magazine
Latest Screens
Rocker`s Revenge
Stug 3 Stormartillery
Iljuschin 2
Galactic Strike X
Who's Online Now
3 registered members (NewbieZorro, TipmyPip, AndrewAMD), 14,749 guests, and 7 spiders.
Key: Admin, Global Mod, Mod
Newest Members
krishna, DrissB, James168, Ed_Love, xtns
19168 Registered Users
Previous Thread
Next Thread
Print Thread
Rate Thread
Scrollable TEXT panels #338493
08/18/10 15:18
08/18/10 15:18
Joined: Feb 2008
Posts: 3,232
Australia
EvilSOB Offline OP
Expert
EvilSOB  Offline OP
Expert

Joined: Feb 2008
Posts: 3,232
Australia
Hi all.

Heres a simple, but I think useful, little system Ive spent the last few days on.
Paracharlie asked about a TEXT object with scrolling (for an RPG project from memory).

That inspired me to this project. Its become more of a 'multi-player chat' system,
but would easily do what he was asking for, just by ignoring the text-entry stage.


On to the project!

This system lets you attach a 'history' object onto an existing TEXT, so when you
add text to it (using "iChat_add_line()"), it will also be added to the history,
and you can scroll forward or back both by calling "iChat_scroll()", and from the
text-entry mode using the arrow keys, pgup and pgdn.

It comes complete with a interactive demo, so just save the script as "iChat_demo.c"
or something to that effect, and run it...
It should contain enough documentation to keep everyone happpy...

As usual, Im always looking for bug reports, and suggested improvements,
usage questions, or any other help.

Code:
#include <acknex.h>
#include <default.c>
////
//
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//																										//
//		iChat with Demo	-	FunctionLibrary for attaching a scrollable history	//
//									and text-entry system to an existing TEXT object.	//
//																										//
//============================================================================//
//																										//
//		Author	:	EvilSOB																		//
//		Written	: 	18-08-2010																	//
//		Version	:	1.0																			//
//																										//
//		Inspired by a request by Paracharlie for an RPG chat window.				//
//																										//
//																										//
//		Requirements:	Acknex.h																	//
//																										//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//																										//
//		TEXT* iChat_Create(TEXT* iChat_text, int max_hist);							//
//																										//
//----------------------------------------------------------------------------//
//		This function creates a new, empty iChat handler system, and attaches	//
//		it to the supplied "iChat_text" TEXT object.										//
//		"max_hist" specifies how many lines in total can be stored by the iChat	//
//		history before being auto-truncated when it reaches capacity.				//
//		This number also includes the lines on displayed at any time.				//
//		"iChat_text".skill_x now contains a void* to the history TEXT object.	//
//		"iChat_text".skill_y contains a read-only count of the number of used	//
//		contained in the iChat history.														//
//		Returns a pointer to the history TEXT on success, or FALSE on failure.	//
//		Fails when "iChat_text" is invalid, or "max_history" is less than 1.		//
//----------------------------------------------------------------------------//
//		Usage:																						//
//			iChat_Create(dialog_txt, 200);													//
//			//this code creates a new, empty iChat attached to TEXT* dialog_txt	//
//																										//
//																										//
//																										//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//																										//
//		var iChat_Destroy(TEXT* iChat_text);												//
//																										//
//----------------------------------------------------------------------------//
//		Frees all strings in use by the iChat-table attached to "iChat_text".	//
//		It then frees the iChat-table itself, and clears the skill_x & skill_y	//
//		values stored in the "iChat_text" TEXT object.	The "iChat_text" is 		//
//		left otherwise unchanged.																//
//		Returns TRUE on sucess, or FALSE on failure. Fails if "iChat_text"		//
//		is invalid, or does not have a iChat object attached.							//
//----------------------------------------------------------------------------//
//		Usage:																						//
//			iChat_Destroy(dialog_txt);															//
//			//sys_free's all resources used by the iChat list attached to the		//
//			//TEXT object 'dialog_text'.														//
//																										//
//																										//
//																										//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//																										//
//		var iChat_scroll(TEXT* iChat_text, int lines);									//
//																										//
//----------------------------------------------------------------------------//
//		Scrolls the displayed area of iChat-table by "lines" number of lines.	//
//		Negative is how many lines to scroll UP by, positive numbers are DOWN.	//
//		If "lines" is set higher or lower than the actual number of used lines,	//
//		it will merely scroll to the Top or End of the table with no errors.		//
//		If "lines" is supplied as ZERO, it merely forces a redraw of the table.	//
//		Returns TRUE on sucess, or FALSE on failure. Fails if "iChat_text"		//
//		is invalid, or does not have a iChat object attached.							//
//----------------------------------------------------------------------------//
//		Usage:																						//
//			if(key_cuu)	iChat_scroll(dialog_txt, -1);	//scroll up by one line		//
//			if(key_cud)	iChat_scroll(dialog_txt,  1);	//scroll down by one line	//
//																										//
//																										//
//																										//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//																										//
//		var iChat_add_line(TEXT* iChat_text, STRING* text_line);						//
//																										//
//----------------------------------------------------------------------------//
//		Appends the supplied line of text to the END of "iChat_text"'s history.	//
//		If the end of the table is displayed, the table will automatically		//
//		 'scroll up' by one line. Does not interfere with	text-entry-mode.		//
//		Returns the new total line-count on sucess, or FALSE on failure.			//
//		Fails if "iChat_text" is invalid, or does not have a iChat object 		//
//		attached, or "text_line is invalid.													//
//----------------------------------------------------------------------------//
//		Usage:																						//
//			iChat_add_line(dialog_text, "New line added!");								//
//																										//
//																										//
// 	NOTE:	When the history 'hits' capacity, then the oldest 10% of the 		//
//				history lines will be truncated in one step.								//
//				This percentage can be overridden by redefining "iChat_TRUNC"		//
//				from its existing value of '10'% to a different percentage.			//
//				If any truncated lines are on display, the displayed-area will		//
//				auto-scroll to remain at the top of the list...							//
//																										//
//																										//
//																										//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//																										//
//		void iChat_start_input(TEXT* iChat_text, int width, int pos);				//
//																										//
//----------------------------------------------------------------------------//
//		Starts text-entry-mode in "iChat_text" at the line-number("pos").			//
//		If "pos" is -1, then cursor is positioned after the last USED line.		//
//		If "pos" is larger than the "iChat_text".strings, its set at the end.	//
//		"width" is the maximum length of the input string.(see 'inkey' in manual)
//		Input-Keys :: 'ESC' or 'TAB' keys abort text-entry mode.						//
//		Input-Keys :: 'Enter' or 'Return' to accept, blank lines ARE accepted.	//
//		Input-Keys :: 'Up' and 'Down' arrow keys scroll up & down by ONE line.	//
//		Input-Keys :: 'PgUp' and 'PgDn' keys scroll up & down by FIVE lines.		//
//		NOTE: PgUp & PgDn scroll sizes can be adjusted at any time by varying	//
//		the value of the variable "iChat_Page"(type long).								//
//----------------------------------------------------------------------------//
//		Usage:																						//
//			iChat_start_input(dialog_text, 40, -1);										//
//			//this starts an 'inkey()' text-entry at the next available line.		//
//																										//
//																										//
//																										//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//		Global Parameters and Controls														//
//																										//
#define	iChat_TRUNC		10		//can be over-ridden safely							//
long		iChat_Page	=	5;		//can be over-ridden safely							//
int		iChat_Input =	-1;	//text-entry-mode line-number !DO NOT DISTURB!	//
//																										//
////////////////////////////////////////////////////////////////////////////////
//
//
//
////////////////////////////////////////////////////////////////////////////////
//		Start of iChat Functions																//
////////////////////////////////////////////////////////////////////////////////
//
TEXT* iChat_Create(TEXT* iChat_text, int max_hist)
{	// Creates a new iChat handler system in the "iChat_text" TEXT object.
	// skill_x now contains a void* to the history TEXT object.
	// skill_y contains the number of lines contained in the history.(READ-ONLY)
	// Returns a pointer to the history TEXT on success, or FALSE on failure.
	// [ NOTE: history.skill_x=entries count, history.skill_y=scroll position ]
	//
	if((!iChat_text)||(max_hist<=0))		return(0);	//FAILED: Invalid parameters
	iChat_text.skill_x = (void*)txt_create(max_hist, iChat_text.layer);
	iChat_text.skill_y = 0;
	return((TEXT*)iChat_text.skill_x);	//SUCCESS:
}
//
////////////////////////////////////////////////////////////////////////////////
//
var iChat_Destroy(TEXT* iChat_text)
{	// Frees all strings in use by the history-table, and then frees the 
	//	history-table itself. And clears the skill_x & skill_y workspace values
	//	stored in the "iChat_text" TEXT. The "iChat_text" is otherwise unchanged.
	// Returns TRUE on sucess, or FALSE on failure.
	//
	if(!iChat_text)						return(0);	//FAILED: Invalid parameters
	if(!iChat_text.skill_x)				return(0);	//FAILED: Not an iChat object
	if(((long)(handle((void*)iChat_text.skill_x)>>14)&31)!=20)
												return(0);	//FAILED: Not an iChat object
	int i;	TEXT*	iChat_hist = (TEXT*)iChat_text.skill_x;
	//
	for(i=0;i<iChat_hist.strings;i++)	//free history
		if((iChat_hist.pstring)[i])	str_remove((iChat_hist.pstring)[i]);
	txt_remove(iChat_hist);		//remove cleared history object
	for(i=0;i<iChat_text.strings;i++)	//clear chat-text
		if((iChat_text.pstring)[i])	str_cpy((iChat_text.pstring)[i],"");
	iChat_text.skill_x = iChat_text.skill_y = 0;
	return(1);	//SUCCESS:
}
//
////////////////////////////////////////////////////////////////////////////////
//
var iChat_scroll(TEXT* iChat_text, int lines)
{	// Scrolls the displayed section of table by the selected number of lines.
	// Negative is how many lines to scroll UP by, positive numbers are DOWN.
	// If "lines" is set higher or lower than the actual number of used lines,
	//	it will only scroll to the Top or End of the table, with no errors.
	//	If "lines" is supplied as ZERO, it merely forces a re-draw of the table.
	//
	if(!iChat_text)						return(0);	//FAILED: Invalid parameters
	if(!iChat_text.skill_x)				return(0);	//FAILED: Not an iChat object
	if(((long)(handle((void*)iChat_text.skill_x)>>14)&31)!=20)
												return(0);	//FAILED: Not an iChat object
	int i;	TEXT*	iChat_hist = (TEXT*)iChat_text.skill_x;
	//
	var limit = maxv(0, iChat_hist.skill_x - iChat_text.strings + 1);
	iChat_hist.skill_y = clamp(iChat_hist.skill_y + lines, 0, limit);
	if((iChat_Input>-1)&&(iChat_Input<iChat_text.strings-1))	//shuffle input?
	{	STRING* tmp_ptr = (iChat_text.pstring)[iChat_Input];
		(iChat_text.pstring)[iChat_Input]   = (iChat_text.pstring)[iChat_Input+1];
		(iChat_text.pstring)[iChat_Input+1] = tmp_ptr;	iChat_Input++;				 }
	for(i=0; i<(iChat_text.strings-1); i++)
	{	int idx = i + iChat_hist.skill_y;	//calculate offset
		if((idx<0)||(idx>=iChat_hist.skill_x))	break;	//outside history limit
		if((iChat_hist.pstring)[idx].length)
			str_cpy((iChat_text.pstring)[i], (iChat_hist.pstring)[idx]);	}
	return(1);	//SUCCESS:
}
//
////////////////////////////////////////////////////////////////////////////////
//
var iChat_add_line(TEXT* iChat_text, STRING* text_line)
{	// Appends the supplied line of text to the END of "iChat_text".
	// If the last line is visible, the table will automatically 'scroll up'.
	// Returns TRUE on sucess, or FALSE on failure.
	//
	if(!iChat_text)						return(0);	//FAILED: Invalid parameters
	if(!iChat_text.skill_x)				return(0);	//FAILED: Not an iChat object
	if(((long)(handle((void*)iChat_text.skill_x)>>14)&31)!=20)
												return(0);	//FAILED: Not an iChat object
	int i;	TEXT*	iChat_hist = (TEXT*)iChat_text.skill_x;
	//
	if(iChat_hist.skill_x==iChat_hist.strings)	//if table full
	{	int i, del=iChat_hist.strings/maxv(1,iChat_TRUNC);	//phase out oldest ??%
		for(i=0; i<iChat_hist.strings; i++)
		{	if(i<(iChat_hist.strings-del))
				str_cpy((iChat_hist.pstring)[i], (iChat_hist.pstring)[i+del]);
			else	str_cpy((iChat_hist.pstring)[i], "");						  }
		iChat_hist.skill_x -= del;	iChat_hist.skill_y -= del;				  }
	str_cpy((iChat_hist.pstring)[iChat_hist.skill_x], _chr(text_line));
	iChat_text.skill_y = iChat_hist.skill_x++;			
	if(!(iChat_hist.skill_y+(iChat_text.strings-iChat_hist.skill_x)))
			iChat_scroll(iChat_text, 1);	//auto-scroll if at end
	else	iChat_scroll(iChat_text, 0);	//otherwise just re-draw
	return(iChat_text.skill_y);
}
//
////////////////////////////////////////////////////////////////////////////////
//
//
void	iChat_start_input(TEXT* iChat_text, int width, int pos)
{	// Starts a string-entry in "iChat_text" at the chosen line-number("pos").
	// If "pos" is -1, then cursor is positioned after the last USED line.
	// If "pos" is larger than the iChat_text length, it is set at the last line.
	// Input-Keys :: 'ESC' or 'TAB'to abort, and scroll up & down are active.
	// Input-Keys :: 'Enter' or 'Return' to accept, blank lines WILL be accepted.
	//	"width" is the maximum length of the input string.(see 'inkey' in manual)
	//
	if(!iChat_text)						return(0);	//FAILED: Invalid parameters
	if(!iChat_text.skill_x)				return(0);	//FAILED: Not an iChat object
	if(((long)(handle((void*)iChat_text.skill_x)>>14)&31)!=20)
												return(0);	//FAILED: Not an iChat object
	int i;	TEXT*	iChat_hist = (TEXT*)iChat_text.skill_x;
	if(proc_status(iChat_start_input))	return;	//already in use
	//
	STRING* str_width=str_create("");	str_cat_num(str_width,"#%.0f",width);
	if(pos == -1)	pos = minv(iChat_text.strings-1, iChat_text.skill_y);
	while(1)	{	iChat_Input = minv(pos, iChat_text.strings-1);	//track input pos
		STRING* input = (iChat_text.pstring)[iChat_Input];	//shortcut to input
		str_cpy(input, _chr(str_width));		//insert blank line for input
		var end = inkey(input);				//type into supplied text-line
		if((end==9)||(end==27))	break;	//aborted by user. (TAB or ESC pressed)
		else if(end==1)	break;	//input aborted elsewhere(inkey_active==0)
		else if(end==72)	iChat_scroll(iChat_text, -1);	//small up   (key_up)
		else if(end==73)	iChat_scroll(iChat_text, -iChat_Page);	//  (key_pgup)
		else if(end==80)	iChat_scroll(iChat_text,  1);	//small down (key_up)
		else if(end==81)	iChat_scroll(iChat_text,  iChat_Page);	//  (key_pgup)
		else {	pos = iChat_Input+1;		iChat_Input=-1;	//store entered line
					iChat_add_line(iChat_text, input);	  }	//auto-increment line
	}
	str_remove(str_width);	//clear up workspace;
}
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//
//	Purely DEMO code from here on in.....
//
//
//
COLOR* white = { blue=255; green=255; red=255;	}
COLOR* lgrey = { blue=127; green=127; red=127;	}
FONT*		iChat_font = "Arial#16";
//
BMAP* 	chat_back = "#230x200x24";
PANEL*	iChat_pan  = {	layer=100;	pos_x=300; pos_y=150;	bmap=chat_back;
								event=iChat_txt_input;										}
TEXT*		iChat_txt = {	layer=101;	strings=11;		font=iChat_font;			}
//
//
//
//
void toggle_iChat_pan()			//enable open chat panel
{
	if(is(iChat_pan,SHOW))
	{	reset(iChat_pan,SHOW);	reset(iChat_txt,SHOW);	}	//toggle OFF
	else
	{	set(iChat_pan,SHOW);		set(iChat_txt,SHOW);			//toggle ON
		iChat_txt.pos_x=iChat_pan.pos_x+10;						//and position TEXT
		iChat_txt.pos_y=iChat_pan.pos_y+10;				}		//to PANEL position
}
//
//
//
//
void iChat_txt_input()	{	iChat_start_input(iChat_txt, 30, -1);	}	//pan-event
//
//
//
//
void toggle_chatter()	//enable "synthesised? other people conversing
{	if(proc_status(toggle_chatter))	{	proc_kill(4);	return;	}
	while(1)	
	{	wait(-random(5));
		switch(integer(random(6)))
		{	case 0:
				iChat_add_line(iChat_txt, "[george] Hi guys, wots happening?");
			case 1:
				iChat_add_line(iChat_txt, "[frank] Wheres everybody gone?");
			case 2:
				iChat_add_line(iChat_txt, "[noobie] Ahh! He got meeeee!");
			case 3:
				iChat_add_line(iChat_txt, "[leet] Mwahahahaaa!  Got Ya!!!");
			case 4:
				iChat_add_line(iChat_txt, "[jacob] No-one will find me here");
			case 5:
				iChat_add_line(iChat_txt, "[EvilSOB] I am THE MASTER!");
}	}	}
//
//
//
//
//
void main()
{
	mouse_mode=4;	level_load(NULL);		wait(5);		diag("\n\n\n");
	//
	on_t	= toggle_iChat_pan;		//enable open chat panel
	on_c	= toggle_chatter;			//enable "synthesised? other people conversing
	//
	//
	iChat_Create(iChat_txt, 20);	//make ordinary TEXT object into an iChat object
	iChat_add_line(iChat_txt, "iChat_Panel initialised!");	//optional first line
	//
	//////////////////////////////////////////////////////////////////////////////
	TEXT* iChat_hist = (TEXT*)iChat_txt.skill_x;		 //show history for debug/demo
	iChat_hist.pos_y=190;	set(iChat_hist, SHOW);	 //show history for debug/demo
	//////////////////////////////////////////////////////////////////////////////
	//
	while(1)
	{
		draw_text("Press 'T' to toggle Chat-Panel visibility.", 5, 5, white);
		draw_text("Left-Click on panel to trigger Text-Entry.", 5, 25, white);
		draw_text("ENTIRE history display."	, 5, 150, lgrey);
		draw_text("[ normally hidden ]"		, 5, 170, lgrey);
		//
		//
		wait(1);
	}
	//
}




"There is no fate but what WE make." - CEO Cyberdyne Systems Corp.
A8.30.5 Commercial
Re: Scrollable TEXT panels [Re: EvilSOB] #338502
08/18/10 16:53
08/18/10 16:53
Joined: Dec 2006
Posts: 1,086
Queensland - Australia
Nidhogg Offline
Serious User
Nidhogg  Offline
Serious User

Joined: Dec 2006
Posts: 1,086
Queensland - Australia
Thankyou EvilSOB this will come in handy later.


Windows XP SP3
Intel Dual Core CPU: E5200 @ 2.5GHz
4.00GB DDR3 Ram
ASUS P5G41T-M LX
PCIE x16 GeForce GTS 450 1Gb
SB Audigy 4
Spyware Doctor with AntiVirus
Re: Scrollable TEXT panels [Re: Nidhogg] #338551
08/19/10 00:06
08/19/10 00:06
Joined: Mar 2009
Posts: 146
USA
P
paracharlie Offline
Member
paracharlie  Offline
Member
P

Joined: Mar 2009
Posts: 146
USA
Wow EvilSOB, I dont know how much to say thank you. I've already started plugging things into my rpg and I have to say that everything concerning strings now looks very professional, just like the pro's.
Both you and MrGuest have just made another huge contribution to the whole community. I hope others see the power, ease, and flexibility in these "modules."


A8 Commercial

Moderated by  HeelX, Lukas, rayp, Rei_Ayanami, Superku, Tobias, TWO, VeT 

Gamestudio download | Zorro platform | shop | Data Protection Policy

oP group Germany GmbH | Birkenstr. 25-27 | 63549 Ronneburg / Germany | info (at) opgroup.de

Powered by UBB.threads™ PHP Forum Software 7.7.1