- split the command line buffer off from c_console.cpp and reactivated the check for CONBACK.

This commit is contained in:
Christoph Oelckers 2020-10-10 23:16:08 +02:00
parent 87859e87bf
commit 242a70e610
4 changed files with 390 additions and 355 deletions

View file

@ -0,0 +1,373 @@
/*
** c_commandbuffer.cpp
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** Copyright 2010-2020 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// for inclusion in c_console.cpp.
struct FCommandBuffer
{
private:
std::u32string Text;
unsigned CursorPos = 0;
unsigned StartPos = 0; // First character to display
unsigned CursorPosCells = 0;
unsigned StartPosCells = 0;
std::u32string YankBuffer; // Deleted text buffer
public:
bool AppendToYankBuffer = false; // Append consecutive deletes to buffer
int ConCols;
FCommandBuffer() = default;
FCommandBuffer(const FCommandBuffer &o)
{
Text = o.Text;
CursorPos = o.CursorPos;
StartPos = o.StartPos;
}
FString GetText() const
{
FString build;
for (auto chr : Text) build.AppendCharacter(chr);
return build;
}
size_t TextLength() const
{
return Text.length();
}
void Draw(int x, int y, int scale, bool cursor)
{
if (scale == 1)
{
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c', TAG_DONE);
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
&Text[StartPos], TAG_DONE);
if (cursor)
{
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
y, '\xb', TAG_DONE);
}
}
else
{
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c',
DTA_VirtualWidth, twod->GetWidth() / scale,
DTA_VirtualHeight, twod->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
&Text[StartPos],
DTA_VirtualWidth, twod->GetWidth() / scale,
DTA_VirtualHeight, twod->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
if (cursor)
{
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
y, '\xb',
DTA_VirtualWidth, twod->GetWidth() / scale,
DTA_VirtualHeight, twod->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
}
}
}
unsigned CalcCellSize(unsigned length)
{
unsigned cellcount = 0;
for (unsigned i = 0; i < length; i++)
{
int w;
NewConsoleFont->GetChar(Text[i], CR_UNTRANSLATED, &w);
cellcount += w / 9;
}
return cellcount;
}
unsigned CharsForCells(unsigned cellin, bool *overflow)
{
unsigned chars = 0;
int cells = cellin;
while (cells > 0)
{
int w;
NewConsoleFont->GetChar(Text[chars++], CR_UNTRANSLATED, &w);
cells -= w / 9;
}
*overflow = (cells < 0);
return chars;
}
void MakeStartPosGood()
{
// Make sure both values point to something valid.
if (CursorPos > Text.length()) CursorPos = (unsigned)Text.length();
if (StartPos > Text.length()) StartPos = (unsigned)Text.length();
CursorPosCells = CalcCellSize(CursorPos);
StartPosCells = CalcCellSize(StartPos);
unsigned LengthCells = CalcCellSize((unsigned)Text.length());
int n = StartPosCells;
unsigned cols = ConCols / active_con_scale(twod);
if (StartPosCells >= LengthCells)
{ // Start of visible line is beyond end of line
n = CursorPosCells - cols + 2;
}
if ((CursorPosCells - StartPosCells) >= cols - 2)
{ // The cursor is beyond the visible part of the line
n = CursorPosCells - cols + 2;
}
if (StartPosCells > CursorPosCells)
{ // The cursor is in front of the visible part of the line
n = CursorPosCells;
}
StartPosCells = std::max(0, n);
bool overflow;
StartPos = CharsForCells(StartPosCells, &overflow);
if (overflow)
{
// We ended up in the middle of a double cell character, so set the start to the following character.
StartPosCells++;
StartPos = CharsForCells(StartPosCells, &overflow);
}
}
void CursorStart()
{
CursorPos = 0;
StartPos = 0;
CursorPosCells = 0;
StartPosCells = 0;
}
void CursorEnd()
{
CursorPos = (unsigned)Text.length();
MakeStartPosGood();
}
private:
void MoveCursorLeft()
{
CursorPos--;
}
void MoveCursorRight()
{
CursorPos++;
}
public:
void CursorLeft()
{
if (CursorPos > 0)
{
MoveCursorLeft();
MakeStartPosGood();
}
}
void CursorRight()
{
if (CursorPos < Text.length())
{
MoveCursorRight();
MakeStartPosGood();
}
}
void CursorWordLeft()
{
if (CursorPos > 0)
{
do MoveCursorLeft();
while (CursorPos > 0 && Text[CursorPos - 1] != ' ');
MakeStartPosGood();
}
}
void CursorWordRight()
{
if (CursorPos < Text.length())
{
do MoveCursorRight();
while (CursorPos < Text.length() && Text[CursorPos] != ' ');
MakeStartPosGood();
}
}
void DeleteLeft()
{
if (CursorPos > 0)
{
MoveCursorLeft();
Text.erase(CursorPos, 1);
MakeStartPosGood();
}
}
void DeleteRight()
{
if (CursorPos < Text.length())
{
Text.erase(CursorPos, 1);
MakeStartPosGood();
}
}
void DeleteWordLeft()
{
if (CursorPos > 0)
{
auto now = CursorPos;
CursorWordLeft();
if (AppendToYankBuffer) {
YankBuffer = Text.substr(CursorPos, now - CursorPos) + YankBuffer;
} else {
YankBuffer = Text.substr(CursorPos, now - CursorPos);
}
Text.erase(CursorPos, now - CursorPos);
MakeStartPosGood();
}
}
void DeleteLineLeft()
{
if (CursorPos > 0)
{
if (AppendToYankBuffer) {
YankBuffer = Text.substr(0, CursorPos) + YankBuffer;
} else {
YankBuffer = Text.substr(0, CursorPos);
}
Text.erase(0, CursorPos);
CursorStart();
}
}
void DeleteLineRight()
{
if (CursorPos < Text.length())
{
if (AppendToYankBuffer) {
YankBuffer += Text.substr(CursorPos, Text.length() - CursorPos);
} else {
YankBuffer = Text.substr(CursorPos, Text.length() - CursorPos);
}
Text.resize(CursorPos);
CursorEnd();
}
}
void AddChar(int character)
{
if (Text.length() == 0)
{
Text += character;
}
else
{
Text.insert(CursorPos, 1, character);
}
CursorPos++;
MakeStartPosGood();
}
void AddString(FString clip)
{
if (clip.IsNotEmpty())
{
// Only paste the first line.
long brk = clip.IndexOfAny("\r\n\b");
std::u32string build;
if (brk >= 0)
{
clip.Truncate(brk);
}
auto strp = (const uint8_t*)clip.GetChars();
while (auto chr = GetCharFromString(strp)) build += chr;
if (Text.length() == 0)
{
Text = build;
}
else
{
Text.insert(CursorPos, build);
}
CursorPos += (unsigned)build.length();
MakeStartPosGood();
}
}
void SetString(const FString &str)
{
Text.clear();
auto strp = (const uint8_t*)str.GetChars();
while (auto chr = GetCharFromString(strp)) Text += chr;
CursorEnd();
MakeStartPosGood();
}
void AddYankBuffer()
{
if (YankBuffer.length() > 0)
{
if (Text.length() == 0)
{
Text = YankBuffer;
}
else
{
Text.insert(CursorPos, YankBuffer);
}
CursorPos += (unsigned)YankBuffer.length();
MakeStartPosGood();
}
}
};

View file

@ -68,6 +68,9 @@
#include "menustate.h" #include "menustate.h"
#include "i_interface.h" #include "i_interface.h"
#include "vm.h" #include "vm.h"
#include "gi.h"
#include "texturemanager.h"
#include "c_commandbuffer.h"
#define LEFTMARGIN 8 #define LEFTMARGIN 8
#define RIGHTMARGIN 8 #define RIGHTMARGIN 8
@ -92,7 +95,7 @@ static bool TabbedList; // True if tab list was shown
CVAR(Bool, con_notablist, false, CVAR_ARCHIVE) CVAR(Bool, con_notablist, false, CVAR_ARCHIVE)
static FGameTexture *conback; static FTextureID conback;
static uint32_t conshade; static uint32_t conshade;
static bool conline; static bool conline;
@ -101,7 +104,6 @@ extern bool advancedemo;
extern FBaseCVar *CVars; extern FBaseCVar *CVars;
extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE]; extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
unsigned ConCols;
int ConWidth; int ConWidth;
bool vidactive = false; bool vidactive = false;
bool cursoron = false; bool cursoron = false;
@ -177,342 +179,6 @@ struct History
FString String; FString String;
}; };
struct FCommandBuffer
{
private:
std::u32string Text;
unsigned CursorPos = 0;
unsigned StartPos = 0; // First character to display
unsigned CursorPosCells = 0;
unsigned StartPosCells = 0;
std::u32string YankBuffer; // Deleted text buffer
public:
bool AppendToYankBuffer = false; // Append consecutive deletes to buffer
FCommandBuffer() = default;
FCommandBuffer(const FCommandBuffer &o)
{
Text = o.Text;
CursorPos = o.CursorPos;
StartPos = o.StartPos;
}
FString GetText() const
{
FString build;
for (auto chr : Text) build.AppendCharacter(chr);
return build;
}
size_t TextLength() const
{
return Text.length();
}
void Draw(int x, int y, int scale, bool cursor)
{
if (scale == 1)
{
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c', TAG_DONE);
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
&Text[StartPos], TAG_DONE);
if (cursor)
{
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
y, '\xb', TAG_DONE);
}
}
else
{
DrawChar(twod, CurrentConsoleFont, CR_ORANGE, x, y, '\x1c',
DTA_VirtualWidth, twod->GetWidth() / scale,
DTA_VirtualHeight, twod->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
DrawText(twod, CurrentConsoleFont, CR_ORANGE, x + CurrentConsoleFont->GetCharWidth(0x1c), y,
&Text[StartPos],
DTA_VirtualWidth, twod->GetWidth() / scale,
DTA_VirtualHeight, twod->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
if (cursor)
{
DrawChar(twod, CurrentConsoleFont, CR_YELLOW,
x + CurrentConsoleFont->GetCharWidth(0x1c) + (CursorPosCells - StartPosCells) * CurrentConsoleFont->GetCharWidth(0xb),
y, '\xb',
DTA_VirtualWidth, twod->GetWidth() / scale,
DTA_VirtualHeight, twod->GetHeight() / scale,
DTA_KeepRatio, true, TAG_DONE);
}
}
}
unsigned CalcCellSize(unsigned length)
{
unsigned cellcount = 0;
for (unsigned i = 0; i < length; i++)
{
int w;
NewConsoleFont->GetChar(Text[i], CR_UNTRANSLATED, &w);
cellcount += w / 9;
}
return cellcount;
}
unsigned CharsForCells(unsigned cellin, bool *overflow)
{
unsigned chars = 0;
int cells = cellin;
while (cells > 0)
{
int w;
NewConsoleFont->GetChar(Text[chars++], CR_UNTRANSLATED, &w);
cells -= w / 9;
}
*overflow = (cells < 0);
return chars;
}
void MakeStartPosGood()
{
// Make sure both values point to something valid.
if (CursorPos > Text.length()) CursorPos = (unsigned)Text.length();
if (StartPos > Text.length()) StartPos = (unsigned)Text.length();
CursorPosCells = CalcCellSize(CursorPos);
StartPosCells = CalcCellSize(StartPos);
unsigned LengthCells = CalcCellSize((unsigned)Text.length());
int n = StartPosCells;
unsigned cols = ConCols / active_con_scale(twod);
if (StartPosCells >= LengthCells)
{ // Start of visible line is beyond end of line
n = CursorPosCells - cols + 2;
}
if ((CursorPosCells - StartPosCells) >= cols - 2)
{ // The cursor is beyond the visible part of the line
n = CursorPosCells - cols + 2;
}
if (StartPosCells > CursorPosCells)
{ // The cursor is in front of the visible part of the line
n = CursorPosCells;
}
StartPosCells = std::max(0, n);
bool overflow;
StartPos = CharsForCells(StartPosCells, &overflow);
if (overflow)
{
// We ended up in the middle of a double cell character, so set the start to the following character.
StartPosCells++;
StartPos = CharsForCells(StartPosCells, &overflow);
}
}
void CursorStart()
{
CursorPos = 0;
StartPos = 0;
CursorPosCells = 0;
StartPosCells = 0;
}
void CursorEnd()
{
CursorPos = (unsigned)Text.length();
MakeStartPosGood();
}
private:
void MoveCursorLeft()
{
CursorPos--;
}
void MoveCursorRight()
{
CursorPos++;
}
public:
void CursorLeft()
{
if (CursorPos > 0)
{
MoveCursorLeft();
MakeStartPosGood();
}
}
void CursorRight()
{
if (CursorPos < Text.length())
{
MoveCursorRight();
MakeStartPosGood();
}
}
void CursorWordLeft()
{
if (CursorPos > 0)
{
do MoveCursorLeft();
while (CursorPos > 0 && Text[CursorPos - 1] != ' ');
MakeStartPosGood();
}
}
void CursorWordRight()
{
if (CursorPos < Text.length())
{
do MoveCursorRight();
while (CursorPos < Text.length() && Text[CursorPos] != ' ');
MakeStartPosGood();
}
}
void DeleteLeft()
{
if (CursorPos > 0)
{
MoveCursorLeft();
Text.erase(CursorPos, 1);
MakeStartPosGood();
}
}
void DeleteRight()
{
if (CursorPos < Text.length())
{
Text.erase(CursorPos, 1);
MakeStartPosGood();
}
}
void DeleteWordLeft()
{
if (CursorPos > 0)
{
auto now = CursorPos;
CursorWordLeft();
if (AppendToYankBuffer) {
YankBuffer = Text.substr(CursorPos, now - CursorPos) + YankBuffer;
} else {
YankBuffer = Text.substr(CursorPos, now - CursorPos);
}
Text.erase(CursorPos, now - CursorPos);
MakeStartPosGood();
}
}
void DeleteLineLeft()
{
if (CursorPos > 0)
{
if (AppendToYankBuffer) {
YankBuffer = Text.substr(0, CursorPos) + YankBuffer;
} else {
YankBuffer = Text.substr(0, CursorPos);
}
Text.erase(0, CursorPos);
CursorStart();
}
}
void DeleteLineRight()
{
if (CursorPos < Text.length())
{
if (AppendToYankBuffer) {
YankBuffer += Text.substr(CursorPos, Text.length() - CursorPos);
} else {
YankBuffer = Text.substr(CursorPos, Text.length() - CursorPos);
}
Text.resize(CursorPos);
CursorEnd();
}
}
void AddChar(int character)
{
if (Text.length() == 0)
{
Text += character;
}
else
{
Text.insert(CursorPos, 1, character);
}
CursorPos++;
MakeStartPosGood();
}
void AddString(FString clip)
{
if (clip.IsNotEmpty())
{
// Only paste the first line.
long brk = clip.IndexOfAny("\r\n\b");
std::u32string build;
if (brk >= 0)
{
clip.Truncate(brk);
}
auto strp = (const uint8_t*)clip.GetChars();
while (auto chr = GetCharFromString(strp)) build += chr;
if (Text.length() == 0)
{
Text = build;
}
else
{
Text.insert(CursorPos, build);
}
CursorPos += (unsigned)build.length();
MakeStartPosGood();
}
}
void SetString(const FString &str)
{
Text.clear();
auto strp = (const uint8_t*)str.GetChars();
while (auto chr = GetCharFromString(strp)) Text += chr;
CursorEnd();
MakeStartPosGood();
}
void AddYankBuffer()
{
if (YankBuffer.length() > 0)
{
if (Text.length() == 0)
{
Text = YankBuffer;
}
else
{
Text.insert(CursorPos, YankBuffer);
}
CursorPos += (unsigned)YankBuffer.length();
MakeStartPosGood();
}
}
};
static FCommandBuffer CmdLine; static FCommandBuffer CmdLine;
#define MAXHISTSIZE 50 #define MAXHISTSIZE 50
@ -601,7 +267,6 @@ CUSTOM_CVAR (Int, msgmidcolor2, CR_BROWN, CVAR_ARCHIVE)
void C_InitConback() void C_InitConback()
{ {
#if 0
conback = TexMan.CheckForTexture ("CONBACK", ETextureType::MiscPatch); conback = TexMan.CheckForTexture ("CONBACK", ETextureType::MiscPatch);
if (!conback.isValid()) if (!conback.isValid())
@ -615,10 +280,6 @@ void C_InitConback()
conshade = 0; conshade = 0;
conline = false; conline = false;
} }
#else
conshade = MAKEARGB(175, 0, 0, 0);
conline = true;
#endif
} }
void C_InitConsole (int width, int height, bool ingame) void C_InitConsole (int width, int height, bool ingame)
@ -636,7 +297,7 @@ void C_InitConsole (int width, int height, bool ingame)
cwidth = cheight = 8; cwidth = cheight = 8;
} }
ConWidth = (width - LEFTMARGIN - RIGHTMARGIN); ConWidth = (width - LEFTMARGIN - RIGHTMARGIN);
ConCols = ConWidth / cwidth; CmdLine.ConCols = ConWidth / cwidth;
if (conbuffer == NULL) conbuffer = new FConsoleBuffer; if (conbuffer == NULL) conbuffer = new FConsoleBuffer;
} }
@ -923,7 +584,7 @@ int PrintString (int iprintlevel, const char *outline)
conbuffer->AddText(printlevel, outline); conbuffer->AddText(printlevel, outline);
if (!(iprintlevel & PRINT_NONOTIFY)) if (!(iprintlevel & PRINT_NONOTIFY))
{ {
if (vidactive && ((iprintlevel & PRINT_NOTIFY) || con_notify_advanced)) if (screen && vidactive && ((iprintlevel & PRINT_NOTIFY) || con_notify_advanced))
{ {
NotifyStrings.AddString(printlevel, outline); NotifyStrings.AddString(printlevel, outline);
} }
@ -1260,15 +921,15 @@ void C_DrawConsole ()
visheight = ConBottom; visheight = ConBottom;
if (conback) if (conback.isValid())
{ {
DrawTexture (twod, conback, 0, visheight - screen->GetHeight(), DrawTexture (twod, TexMan.GetGameTexture(conback), 0, visheight - screen->GetHeight(),
DTA_DestWidth, screen->GetWidth(), DTA_DestWidth, twod->GetWidth(),
DTA_DestHeight, screen->GetHeight(), DTA_DestHeight, twod->GetHeight(),
DTA_ColorOverlay, conshade, DTA_ColorOverlay, conshade,
DTA_Alpha, (/*gamestate != GS_FULLCONSOLE*/true) ? (double)con_alpha : 1., DTA_Alpha, (gamestate != GS_FULLCONSOLE) ? (double)con_alpha : 1.,
DTA_Masked, false, DTA_Masked, false,
TAG_DONE); TAG_DONE);
} }
else else
{ {
@ -1699,7 +1360,6 @@ static bool C_HandleKey (event_t *ev, FCommandBuffer &buffer)
} }
else if (gamestate == GS_FULLCONSOLE) else if (gamestate == GS_FULLCONSOLE)
{ {
gamestate = GS_MENUSCREEN;
C_DoCommand ("menu_main"); C_DoCommand ("menu_main");
} }
else else
@ -2138,7 +1798,7 @@ static bool C_TabCompleteList ()
Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars()); Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars());
x += maxwidth; x += maxwidth;
if (x > ConCols / active_con_scale(twod) - maxwidth) if (x > CmdLine.ConCols / active_con_scale(twod) - maxwidth)
{ {
x = 0; x = 0;
Printf ("\n"); Printf ("\n");

View file

@ -45,6 +45,7 @@ struct gameinfo_t
int gametype; int gametype;
FName mSliderColor; FName mSliderColor;
FString mBackButton; FString mBackButton;
FString TitlePage;
}; };

View file

@ -638,6 +638,7 @@ CCMD(reset2saved)
CCMD(menu_main) CCMD(menu_main)
{ {
if (gamestate == GS_FULLCONSOLE) gamestate = GS_MENUSCREEN;
M_StartControlPanel(true); M_StartControlPanel(true);
M_SetMenu(NAME_Mainmenu, -1); M_SetMenu(NAME_Mainmenu, -1);
} }