added console mark mode

This commit is contained in:
myT 2022-04-30 04:03:47 +02:00
parent 3f189e58be
commit 533e0872c1
9 changed files with 280 additions and 32 deletions

View File

@ -4,6 +4,10 @@ See the end of this file for known issues.
DD Mmm 20 - 1.53 DD Mmm 20 - 1.53
add: console mark mode (selection mode) can be toggled with Ctrl-M
use arrow keys, home/end or page up/down to move the cursor or extend the area when pressing shift
Ctrl-C/enter copies the selected text to the clipboard without color codes and exits mark mode
add: /searchconsole begins a new console search add: /searchconsole begins a new console search
press ctrl-F when the console is down to bring up the command press ctrl-F when the console is down to bring up the command
press (shift-)F3 to find the next match going up or down press (shift-)F3 to find the next match going up or down

View File

@ -37,7 +37,10 @@ static cvar_t* con_drawHelp;
X(Border, "4778B2FF", qtrue, "RGBA color of the border") \ X(Border, "4778B2FF", qtrue, "RGBA color of the border") \
X(Arrow, "4778B2FF", qtrue, "RGBA color of backscroll arrows") \ X(Arrow, "4778B2FF", qtrue, "RGBA color of backscroll arrows") \
X(Shadow, "000000FF", qtrue, "RGBA color of text shadows") \ X(Shadow, "000000FF", qtrue, "RGBA color of text shadows") \
X(MkBG, "BFBFBFFF", qtrue, "RGBA color of the mark background") \
X(MkShadow, "FFFFFF00", qtrue, "RGBA color of the mark text shadows") \
X(Text, "E2E2E2", qfalse, "RGB color of text") \ X(Text, "E2E2E2", qfalse, "RGB color of text") \
X(MkText, "000000", qfalse, "RGB color of mark text") \
X(CVar, "4778B2", qfalse, "RGB color of variable names") \ X(CVar, "4778B2", qfalse, "RGB color of variable names") \
X(Cmd, "4FA7BD", qfalse, "RGB color of command names") \ X(Cmd, "4FA7BD", qfalse, "RGB color of command names") \
X(Value, "E5BC39", qfalse, "RGB color of variable values") \ X(Value, "E5BC39", qfalse, "RGB color of variable values") \
@ -55,6 +58,11 @@ COLOR_LIST( COLOR_LIST_ITEM )
#define CON_TEXTSIZE (256*1024) #define CON_TEXTSIZE (256*1024)
#define COLOR_INVALID '\xFF'
#define COLOR_MKTEXT '\xFE'
#define COLOR_MKSHAD '\xFD'
#define COLOR_SHAD '\xFC'
// con_drawHelp flags // con_drawHelp flags
#define DRAWHELP_ENABLE_BIT 1 #define DRAWHELP_ENABLE_BIT 1
#define DRAWHELP_NOTFOUND_BIT 2 #define DRAWHELP_NOTFOUND_BIT 2
@ -98,6 +106,13 @@ struct console_t {
char searchPattern[256]; char searchPattern[256];
qbool searchLineIndex; qbool searchLineIndex;
qbool searchStarted; qbool searchStarted;
qbool markMode;
int markX; // cursor X: offset in the row
int markY; // cursor Y: row index
int markStartX;
int markStartY;
char markText[CON_TEXTSIZE + CON_TEXTSIZE / 100]; // some extra for line endings and the final newline
}; };
static console_t con; static console_t con;
@ -175,6 +190,12 @@ const float* ConsoleColorFromChar( char ccode )
return colValue; return colValue;
if ( ccode == COLOR_HELP ) if ( ccode == COLOR_HELP )
return colHelp; return colHelp;
if ( ccode == COLOR_MKTEXT )
return colMkText;
if ( ccode == COLOR_MKSHAD )
return colMkShadow;
if ( ccode == COLOR_SHAD )
return colShadow;
return ColorFromChar( ccode ); return ColorFromChar( ccode );
} }
@ -201,6 +222,7 @@ void Con_ToggleConsole_f()
Con_ClearNotify(); Con_ClearNotify();
cls.keyCatchers ^= KEYCATCH_CONSOLE; cls.keyCatchers ^= KEYCATCH_CONSOLE;
con.markMode = qfalse;
} }
/* /*
@ -561,7 +583,7 @@ static void Con_DrawInput()
re.SetColor( colText ); re.SetColor( colText );
SCR_DrawChar( con.xadjust, y, con.cw, con.ch, ']' ); SCR_DrawChar( con.xadjust, y, con.cw, con.ch, ']' );
Field_Draw( &g_consoleField, con.xadjust + con.cw, y, con.cw, con.ch, qtrue ); Field_Draw( &g_consoleField, con.xadjust + con.cw, y, con.cw, con.ch, qtrue, !Con_IsInMarkMode() );
} }
@ -768,20 +790,23 @@ static void Con_DrawSolidConsole( float frac )
SCR_DrawChar( x, scanlines - (SMALLCHAR_HEIGHT * 1.5), SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, Q3_VERSION[i] ); SCR_DrawChar( x, scanlines - (SMALLCHAR_HEIGHT * 1.5), SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, Q3_VERSION[i] );
} }
const int cw = (int)ceilf( con.cw );
const int ch = (int)ceilf( con.ch );
re.SetColor( NULL ); re.SetColor( NULL );
rows = (scanlines - con.ch) / con.ch; rows = (scanlines - ch) / ch;
con.y = scanlines; con.y = scanlines;
y = scanlines - (con.ch * 3); y = scanlines - (ch * 3);
// draw the console text from the bottom up // draw the console text from the bottom up
if (con.display != con.current) { if (con.display != con.current) {
// draw arrows to show the buffer is backscrolled // draw arrows to show the buffer is backscrolled
const int xEnd = ( cls.glconfig.vidWidth - con.xadjust ) / con.cw; const int xEnd = ( cls.glconfig.vidWidth - con.xadjust ) / cw;
re.SetColor( colArrow ); re.SetColor( colArrow );
for (x = 0; x < xEnd; x += 4) for (x = 0; x < xEnd; x += 4)
SCR_DrawChar( con.xadjust + x * con.cw, y, con.cw, con.ch, '^' ); SCR_DrawChar( con.xadjust + x * cw, y, cw, ch, '^' );
y -= con.ch; y -= ch;
--rows; --rows;
} }
@ -790,11 +815,29 @@ static void Con_DrawSolidConsole( float frac )
row--; row--;
} }
char color = COLOR_WHITE; const int markStartX = min( con.markStartX, con.markX );
re.SetColor( ConsoleColorFromChar( color ) ); const int markStartY = min( con.markStartY, con.markY );
const int markEndX = max( con.markStartX, con.markX );
const int markEndY = max( con.markStartY, con.markY );
qbool drawMark = qfalse;
if (con.markMode) {
// draw the mark area background, if any
const float mhu = ( markEndY - markStartY + 1 ) * ch; // unclipped
const float mh = min( mhu, (float)(con.display - markStartY) * ch );
drawMark =
markEndX != markStartX ||
markEndY != markStartY ||
Sys_Milliseconds() % 1000 < 500;
if (drawMark && mh > 0.0f) {
const float mx = con.xadjust + markStartX * cw;
const float my = y - ( con.display - markStartY - 1 ) * ch;
const float mw = ( markEndX - markStartX + 1 ) * cw;
Con_FillRect( mx, my, mw, mh, colMkBG );
}
}
con.rowsVisible = 0; con.rowsVisible = 0;
for (i = 0; i < rows; ++i, --row, y -= con.ch ) for (i = 0; i < rows; ++i, --row, y -= ch )
{ {
if (row < 0) if (row < 0)
break; break;
@ -806,11 +849,19 @@ static void Con_DrawSolidConsole( float frac )
if (y >= 0) if (y >= 0)
con.rowsVisible++; con.rowsVisible++;
const short* text = con.text + (row % con.totallines)*con.linewidth; const short* const text = con.text + (row % con.totallines)*con.linewidth;
const qbool markRow = drawMark && row >= markStartY && row <= markEndY;
re.SetColor( colShadow ); char color = COLOR_INVALID;
for (int j = 0; j < con.linewidth; ++j) { for (int j = 0; j < con.linewidth; ++j) {
SCR_DrawChar( 1 + con.xadjust + j * con.cw, 1 + y, con.cw, con.ch, (text[j] & 0xFF) ); char newColor = COLOR_SHAD;
if (markRow && j >= markStartX && j <= markEndX)
newColor = COLOR_MKSHAD;
if (newColor != color) {
color = newColor;
re.SetColor( ConsoleColorFromChar( color ) );
}
SCR_DrawChar( 1 + con.xadjust + j * cw, 1 + y, cw, ch, (text[j] & 0xFF) );
} }
if ((row % con.totallines) == con.searchLineIndex) { if ((row % con.totallines) == con.searchLineIndex) {
@ -818,13 +869,16 @@ static void Con_DrawSolidConsole( float frac )
SCR_DrawChar( con.xadjust - con.cw, y, con.cw, con.ch, 141 ); SCR_DrawChar( con.xadjust - con.cw, y, con.cw, con.ch, 141 );
} }
re.SetColor( colText ); color = COLOR_INVALID;
for (int j = 0; j < con.linewidth; ++j) { for (int j = 0; j < con.linewidth; ++j) {
if ((text[j] >> 8) != color) { char newColor = text[j] >> 8;
color = (text[j] >> 8); if (markRow && j >= markStartX && j <= markEndX)
newColor = COLOR_MKTEXT;
if (newColor != color) {
color = newColor;
re.SetColor( ConsoleColorFromChar( color ) ); re.SetColor( ConsoleColorFromChar( color ) );
} }
SCR_DrawChar( con.xadjust + j * con.cw, y, con.cw, con.ch, (text[j] & 0xFF) ); SCR_DrawChar( con.xadjust + j * cw, y, cw, ch, (text[j] & 0xFF) );
} }
} }
@ -1022,3 +1076,151 @@ void Con_ContinueSearch( qbool forward )
} }
} }
} }
static void Con_AutoScrollMarkPosition()
{
if (con.markY >= con.display) {
con.display = con.markY + 1;
} else if (con.markY < con.display - con.rowsVisible) {
// we need to jump past 1 more line when we're all the way at the bottom
const int extra = con.display == con.current ? 1 : 0;
con.display += con.markY - ( con.display - con.rowsVisible ) - extra;
}
}
qbool Con_HandleMarkMode( qbool ctrlDown, qbool shiftDown, int key )
{
// ctrl-m = toggle mark mode
if ( ctrlDown && tolower(key) == 'm' ) {
con.markMode = !con.markMode;
if (con.markMode) {
con.markX = 0;
con.markY = con.display - 1;
con.markStartX = 0;
con.markStartY = con.display - 1;
}
return qtrue;
}
if (!con.markMode) {
return qfalse;
}
if (key == K_HOME) {
if (ctrlDown)
return qfalse;
con.markX = 0;
if (!shiftDown) {
con.markStartX = con.markX;
con.markStartY = con.markY;
}
Con_AutoScrollMarkPosition();
return qtrue;
}
if(key == K_END) {
if (ctrlDown)
return qfalse;
con.markX = con.linewidth - 1;
if (!shiftDown) {
con.markStartX = con.markX;
con.markStartY = con.markY;
}
Con_AutoScrollMarkPosition();
return qtrue;
}
if (key == K_LEFTARROW) {
con.markX = max( con.markX - 1, 0 );
if (!shiftDown) {
con.markStartX = con.markX;
con.markStartY = con.markY;
}
Con_AutoScrollMarkPosition();
return qtrue;
}
if (key == K_RIGHTARROW) {
con.markX = min( con.markX + 1, con.linewidth );
if (!shiftDown) {
con.markStartX = con.markX;
con.markStartY = con.markY;
}
Con_AutoScrollMarkPosition();
return qtrue;
}
if (key == K_UPARROW) {
con.markY = max( con.markY - 1, con.totallines - 1 );
if (!shiftDown) {
con.markStartX = con.markX;
con.markStartY = con.markY;
}
Con_AutoScrollMarkPosition();
return qtrue;
}
if (key == K_DOWNARROW) {
con.markY = min( con.markY + 1, con.current - 1 );
if (!shiftDown) {
con.markStartX = con.markX;
con.markStartY = con.markY;
}
Con_AutoScrollMarkPosition();
return qtrue;
}
if (shiftDown && key == K_PGUP) {
con.markY = max( con.markY - 6, con.totallines - 1 );
Con_AutoScrollMarkPosition();
return qtrue;
}
if (shiftDown && key == K_PGDN) {
con.markY = min( con.markY + 6, con.current - 1 );
Con_AutoScrollMarkPosition();
return qtrue;
}
if ((ctrlDown && tolower(key) == 'c') || key == K_ENTER) {
const int markStartX = min( con.markStartX, con.markX );
const int markStartY = min( con.markStartY, con.markY );
const int markEndX = max( con.markStartX, con.markX );
const int markEndY = max( con.markStartY, con.markY );
char* dst = con.markText;
for (int y = markStartY; y <= markEndY; ++y) {
if (y > markStartY) {
*dst++ = '\r';
*dst++ = '\n';
}
const int row = y % con.totallines;
const short* const text = con.text + row * con.linewidth;
for (int x = markStartX; x <= markEndX; ++x) {
*dst++ = text[x] & 0xFF;
}
}
*dst++ = '\0';
Sys_SetClipboardData( con.markText );
con.markMode = qfalse;
return qtrue;
}
// let the original scrolling code run...
if (key == K_PGDN || key == K_PGUP ||
key == K_MWHEELDOWN || key == K_MWHEELUP) {
return qfalse;
}
// ...but block everything else
return qtrue;
}
qbool Con_IsInMarkMode()
{
return con.markMode;
}

View File

@ -213,7 +213,7 @@ EDIT FIELDS
// handles horizontal scrolling and cursor blinking // handles horizontal scrolling and cursor blinking
// position and char sizes are in pixels // position and char sizes are in pixels
void Field_Draw( field_t* edit, int x, int y, int cw, int ch, qbool extColors ) void Field_Draw( field_t* edit, int x, int y, int cw, int ch, qbool extColors, qbool drawCaret )
{ {
int len; int len;
int drawLen; int drawLen;
@ -263,8 +263,8 @@ void Field_Draw( field_t* edit, int x, int y, int cw, int ch, qbool extColors )
firstColor = extColors ? ConsoleColorFromChar( colorCode ) : ColorFromChar( colorCode ); firstColor = extColors ? ConsoleColorFromChar( colorCode ) : ColorFromChar( colorCode );
SCR_DrawStringEx( x, y, cw, ch, str, extColors ? DSC_CONSOLE : DSC_NORMAL, qtrue, firstColor ); SCR_DrawStringEx( x, y, cw, ch, str, extColors ? DSC_CONSOLE : DSC_NORMAL, qtrue, firstColor );
if ( (int)( cls.realtime >> 8 ) & 1 ) { if ( !drawCaret || Sys_Milliseconds() % 500 < 250 ) {
return; // off blink return;
} }
if ( key_overstrikeMode ) { if ( key_overstrikeMode ) {
@ -469,6 +469,10 @@ CONSOLE LINE EDITING
static void Console_Key( int key ) static void Console_Key( int key )
{ {
if ( Con_HandleMarkMode( keys[K_CTRL].down, keys[K_SHIFT].down, key ) ) {
return;
}
// clear auto-completion buffer when not pressing tab // clear auto-completion buffer when not pressing tab
if ( key != K_TAB ) if ( key != K_TAB )
g_consoleField.acOffset = 0; g_consoleField.acOffset = 0;
@ -1226,9 +1230,12 @@ void CL_CharEvent( int key ) {
// distribute the key down event to the appropriate handler // distribute the key down event to the appropriate handler
if ( cls.keyCatchers & KEYCATCH_CONSOLE ) if ( cls.keyCatchers & KEYCATCH_CONSOLE )
{
if ( !Con_IsInMarkMode() )
{ {
Field_CharEvent( &g_consoleField, key ); Field_CharEvent( &g_consoleField, key );
} }
}
else if ( cls.keyCatchers & KEYCATCH_UI ) else if ( cls.keyCatchers & KEYCATCH_UI )
{ {
VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue ); VM_Call( uivm, UI_KEY_EVENT, key | K_CHAR_FLAG, qtrue );

View File

@ -445,6 +445,8 @@ void Con_Top();
void Con_Bottom(); void Con_Bottom();
void Con_Close(); void Con_Close();
void Con_ContinueSearch( qbool forward ); void Con_ContinueSearch( qbool forward );
qbool Con_HandleMarkMode( qbool ctrlDown, qbool shiftDown, int key );
qbool Con_IsInMarkMode();
const float* ConsoleColorFromChar( char ccode ); const float* ConsoleColorFromChar( char ccode );

View File

@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "keycodes.h" #include "keycodes.h"
void Field_Draw( field_t* edit, int x, int y, int cw, int ch, qbool extColors ); void Field_Draw( field_t* edit, int x, int y, int cw, int ch, qbool extColors, qbool drawCaret = qtrue );
extern history_t g_history; extern history_t g_history;
extern field_t g_consoleField; extern field_t g_consoleField;

View File

@ -194,10 +194,16 @@ void* QDECL Sys_LoadDll( const char* name, dllSyscall_t *entryPoint, dllSyscall_
} }
#ifdef DEDICATED #ifdef DEDICATED
char *Sys_GetClipboardData(void)
char* Sys_GetClipboardData()
{ {
return NULL; return NULL;
} }
void Sys_SetClipboardData( const char* )
{
}
#endif #endif
void Sys_Init() void Sys_Init()

View File

@ -608,6 +608,12 @@ char* Sys_GetClipboardData()
} }
void Sys_SetClipboardData( const char* text )
{
SDL_SetClipboardText(text);
}
void Lin_MatchStartAlert() void Lin_MatchStartAlert()
{ {
const int alerts = cl_matchAlerts->integer; const int alerts = cl_matchAlerts->integer;

View File

@ -1107,6 +1107,7 @@ void QDECL Sys_Error( const char *error, ...);
// if it succeeds, returns memory allocated by Z_Malloc // if it succeeds, returns memory allocated by Z_Malloc
// note that this isn't journaled // note that this isn't journaled
char *Sys_GetClipboardData( void ); char *Sys_GetClipboardData( void );
void Sys_SetClipboardData( const char* text );
void Sys_Print( const char *msg ); void Sys_Print( const char *msg );

View File

@ -339,6 +339,26 @@ char *Sys_GetClipboardData( void )
} }
void Sys_SetClipboardData( const char* text )
{
if ( OpenClipboard( NULL ) ) {
const int l = strlen( text );
const HGLOBAL hMemory = GlobalAlloc( GMEM_MOVEABLE, l + 1 );
if ( hMemory ) {
void* const dstMemory = GlobalLock( hMemory );
if ( dstMemory ) {
strcpy( (char*)dstMemory, text );
GlobalUnlock( hMemory );
EmptyClipboard();
SetClipboardData( CF_TEXT, hMemory );
}
GlobalFree( hMemory );
}
CloseClipboard();
}
}
/* /*
======================================================================== ========================================================================