- moved console command line buffer tab completion into their own files.

- layout fixes in load/save menu and confirmation screen.
This commit is contained in:
Christoph Oelckers 2020-10-11 00:16:58 +02:00
parent a6842b6482
commit ef7a7cc39d
13 changed files with 788 additions and 652 deletions

View file

@ -1069,6 +1069,8 @@ set (PCH_SOURCES
common/console/c_consolebuffer.cpp
common/console/c_cvars.cpp
common/console/c_dispatch.cpp
common/console/c_commandbuffer.cpp
common/console/c_tabcomplete.cpp
common/console/c_expr.cpp
common/utility/engineerrors.cpp
common/utility/i_module.cpp

View file

@ -0,0 +1,343 @@
/*
** 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.
**---------------------------------------------------------------------------
**
*/
#include "c_commandbuffer.h"
#include "v_draw.h"
#include "v_2ddrawer.h"
#include "v_font.h"
#include "utf8.h"
FCommandBuffer CmdLine;
FCommandBuffer::FCommandBuffer(const FCommandBuffer &o)
{
Text = o.Text;
CursorPos = o.CursorPos;
StartPos = o.StartPos;
}
FString FCommandBuffer::GetText() const
{
FString build;
for (auto chr : Text) build.AppendCharacter(chr);
return build;
}
void FCommandBuffer::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 FCommandBuffer::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 FCommandBuffer::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 FCommandBuffer::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 FCommandBuffer::CursorStart()
{
CursorPos = 0;
StartPos = 0;
CursorPosCells = 0;
StartPosCells = 0;
}
void FCommandBuffer::CursorEnd()
{
CursorPos = (unsigned)Text.length();
MakeStartPosGood();
}
void FCommandBuffer::CursorLeft()
{
if (CursorPos > 0)
{
MoveCursorLeft();
MakeStartPosGood();
}
}
void FCommandBuffer::CursorRight()
{
if (CursorPos < Text.length())
{
MoveCursorRight();
MakeStartPosGood();
}
}
void FCommandBuffer::CursorWordLeft()
{
if (CursorPos > 0)
{
do MoveCursorLeft();
while (CursorPos > 0 && Text[CursorPos - 1] != ' ');
MakeStartPosGood();
}
}
void FCommandBuffer::CursorWordRight()
{
if (CursorPos < Text.length())
{
do MoveCursorRight();
while (CursorPos < Text.length() && Text[CursorPos] != ' ');
MakeStartPosGood();
}
}
void FCommandBuffer::DeleteLeft()
{
if (CursorPos > 0)
{
MoveCursorLeft();
Text.erase(CursorPos, 1);
MakeStartPosGood();
}
}
void FCommandBuffer::DeleteRight()
{
if (CursorPos < Text.length())
{
Text.erase(CursorPos, 1);
MakeStartPosGood();
}
}
void FCommandBuffer::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 FCommandBuffer::DeleteLineLeft()
{
if (CursorPos > 0)
{
if (AppendToYankBuffer) {
YankBuffer = Text.substr(0, CursorPos) + YankBuffer;
} else {
YankBuffer = Text.substr(0, CursorPos);
}
Text.erase(0, CursorPos);
CursorStart();
}
}
void FCommandBuffer::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 FCommandBuffer::AddChar(int character)
{
if (Text.length() == 0)
{
Text += character;
}
else
{
Text.insert(CursorPos, 1, character);
}
CursorPos++;
MakeStartPosGood();
}
void FCommandBuffer::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 FCommandBuffer::SetString(const FString &str)
{
Text.clear();
auto strp = (const uint8_t*)str.GetChars();
while (auto chr = GetCharFromString(strp)) Text += chr;
CursorEnd();
MakeStartPosGood();
}
void FCommandBuffer::AddYankBuffer()
{
if (YankBuffer.length() > 0)
{
if (Text.length() == 0)
{
Text = YankBuffer;
}
else
{
Text.insert(CursorPos, YankBuffer);
}
CursorPos += (unsigned)YankBuffer.length();
MakeStartPosGood();
}
}

View file

@ -0,0 +1,63 @@
#include <string>
#include "zstring.h"
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);
FString GetText() const;
size_t TextLength() const
{
return Text.length();
}
void Draw(int x, int y, int scale, bool cursor);
unsigned CalcCellSize(unsigned length);
unsigned CharsForCells(unsigned cellin, bool *overflow);
void MakeStartPosGood();;
void CursorStart();
void CursorEnd();
private:
void MoveCursorLeft()
{
CursorPos--;
}
void MoveCursorRight()
{
CursorPos++;
}
public:
void CursorLeft();
void CursorRight();
void CursorWordLeft();
void CursorWordRight();
void DeleteLeft();
void DeleteRight();
void DeleteWordLeft();
void DeleteLineLeft();
void DeleteLineRight();
void AddChar(int character);
void AddString(FString clip);
void SetString(const FString &str);
void AddYankBuffer();
};
extern FCommandBuffer CmdLine;

View file

@ -36,6 +36,7 @@
#include <stdarg.h>
#include "basics.h"
#include "c_tabcomplete.h"
struct event_t;
@ -81,9 +82,6 @@ void C_MidPrint (FFont *font, const char *message, bool bold = false);
bool C_Responder (event_t *ev);
void C_AddTabCommand (const char *name);
void C_RemoveTabCommand (const char *name);
void C_ClearTabCommands(); // Removes all tab commands
void C_SetNotifyFontScale(double scale);
extern const char *console_bar;

View file

@ -0,0 +1,322 @@
/*
** c_tabcomplete.cpp
** Tab completion code
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** 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.
**---------------------------------------------------------------------------
**
*/
#include "name.h"
#include "tarray.h"
#include "zstring.h"
#include "c_commandbuffer.h"
#include "c_cvars.h"
#include "c_tabcomplete.h"
#include "printf.h"
#include "c_dispatch.h"
#include "v_draw.h"
#include <string.h>
struct TabData
{
int UseCount;
FName TabName;
TabData()
: UseCount(0), TabName(NAME_None)
{
}
TabData(const char *name)
: UseCount(1), TabName(name)
{
}
TabData(const TabData &other) = default;
};
static TArray<TabData> TabCommands (TArray<TabData>::NoInit);
static int TabPos; // Last TabCommand tabbed to
static int TabStart; // First char in CmdLine to use for tab completion
static int TabSize; // Size of tab string
bool TabbedLast; // True if last key pressed was tab
bool TabbedList; // True if tab list was shown
CVAR(Bool, con_notablist, false, CVAR_ARCHIVE)
static bool FindTabCommand (const char *name, int *stoppos, int len)
{
FName aname(name);
unsigned int i;
int cval = 1;
for (i = 0; i < TabCommands.Size(); i++)
{
if (TabCommands[i].TabName == aname)
{
*stoppos = i;
return true;
}
cval = strnicmp (TabCommands[i].TabName.GetChars(), name, len);
if (cval >= 0)
break;
}
*stoppos = i;
return (cval == 0);
}
void C_AddTabCommand (const char *name)
{
int pos;
if (FindTabCommand (name, &pos, INT_MAX))
{
TabCommands[pos].UseCount++;
}
else
{
TabData tab(name);
TabCommands.Insert (pos, tab);
}
}
void C_RemoveTabCommand (const char *name)
{
if (TabCommands.Size() == 0)
{
// There are no tab commands that can be removed.
// This is important to skip construction of aname
// in case the NameManager has already been destroyed.
return;
}
FName aname(name, true);
if (aname == NAME_None)
{
return;
}
for (unsigned int i = 0; i < TabCommands.Size(); ++i)
{
if (TabCommands[i].TabName == aname)
{
if (--TabCommands[i].UseCount == 0)
{
TabCommands.Delete(i);
}
break;
}
}
}
void C_ClearTabCommands ()
{
TabCommands.Clear();
}
static int FindDiffPoint (FName name1, const char *str2)
{
const char *str1 = name1.GetChars();
int i;
for (i = 0; tolower(str1[i]) == tolower(str2[i]); i++)
if (str1[i] == 0 || str2[i] == 0)
break;
return i;
}
void C_TabComplete (bool goForward)
{
unsigned i;
int diffpoint;
auto CmdLineText = CmdLine.GetText();
if (!TabbedLast)
{
bool cancomplete;
// Skip any spaces at beginning of command line
for (i = 0; i < CmdLineText.Len(); ++i)
{
if (CmdLineText[i] != ' ')
break;
}
if (i == CmdLineText.Len())
{ // Line was nothing but spaces
return;
}
TabStart = i;
TabSize = (int)CmdLineText.Len() - TabStart;
if (!FindTabCommand(&CmdLineText[TabStart], &TabPos, TabSize))
return; // No initial matches
// Show a list of possible completions, if more than one.
if (TabbedList || con_notablist)
{
cancomplete = true;
}
else
{
cancomplete = C_TabCompleteList ();
TabbedList = true;
}
if (goForward)
{ // Position just before the list of completions so that when TabPos
// gets advanced below, it will be at the first one.
--TabPos;
}
else
{ // Find the last matching tab, then go one past it.
while (++TabPos < (int)TabCommands.Size())
{
if (FindDiffPoint(TabCommands[TabPos].TabName, &CmdLineText[TabStart]) < TabSize)
{
break;
}
}
}
TabbedLast = true;
if (!cancomplete)
{
return;
}
}
if ((goForward && ++TabPos == (int)TabCommands.Size()) ||
(!goForward && --TabPos < 0))
{
TabbedLast = false;
CmdLineText.Truncate(TabSize);
}
else
{
diffpoint = FindDiffPoint(TabCommands[TabPos].TabName, &CmdLineText[TabStart]);
if (diffpoint < TabSize)
{
// No more matches
TabbedLast = false;
CmdLineText.Truncate(TabSize - TabStart);
}
else
{
CmdLineText.Truncate(TabStart);
CmdLineText << TabCommands[TabPos].TabName.GetChars() << ' ';
}
}
CmdLine.SetString(CmdLineText);
CmdLine.MakeStartPosGood();
}
bool C_TabCompleteList ()
{
int nummatches, i;
size_t maxwidth;
int commonsize = INT_MAX;
nummatches = 0;
maxwidth = 0;
auto CmdLineText = CmdLine.GetText();
for (i = TabPos; i < (int)TabCommands.Size(); ++i)
{
if (FindDiffPoint (TabCommands[i].TabName, &CmdLineText[TabStart]) < TabSize)
{
break;
}
else
{
if (i > TabPos)
{
// This keeps track of the longest common prefix for all the possible
// completions, so we can fill in part of the command for the user if
// the longest common prefix is longer than what the user already typed.
int diffpt = FindDiffPoint (TabCommands[i-1].TabName, TabCommands[i].TabName.GetChars());
if (diffpt < commonsize)
{
commonsize = diffpt;
}
}
nummatches++;
maxwidth = std::max (maxwidth, strlen (TabCommands[i].TabName.GetChars()));
}
}
if (nummatches > 1)
{
size_t x = 0;
maxwidth += 3;
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLineText.GetChars());
for (i = TabPos; nummatches > 0; ++i, --nummatches)
{
// [Dusk] Print console commands blue, CVars green, aliases red.
const char* colorcode = "";
FConsoleCommand* ccmd;
if (FindCVar (TabCommands[i].TabName.GetChars(), NULL))
colorcode = TEXTCOLOR_GREEN;
else if ((ccmd = FConsoleCommand::FindByName (TabCommands[i].TabName.GetChars())) != NULL)
{
if (ccmd->IsAlias())
colorcode = TEXTCOLOR_RED;
else
colorcode = TEXTCOLOR_LIGHTBLUE;
}
Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars());
x += maxwidth;
if (x > CmdLine.ConCols / active_con_scale(twod) - maxwidth)
{
x = 0;
Printf ("\n");
}
}
if (x != 0)
{
Printf ("\n");
}
// Fill in the longest common prefix, if it's longer than what was typed.
if (TabSize != commonsize)
{
TabSize = commonsize;
CmdLineText.Truncate(TabStart);
CmdLineText.AppendCStrPart(TabCommands[TabPos].TabName.GetChars(), commonsize);
CmdLine.SetString(CmdLineText);
}
return false;
}
return true;
}

View file

@ -0,0 +1,9 @@
#pragma once
void C_AddTabCommand (const char *name);
void C_RemoveTabCommand (const char *name);
void C_ClearTabCommands(); // Removes all tab commands
void C_TabComplete(bool goForward);
bool C_TabCompleteList();
extern bool TabbedLast; // True if last key pressed was tab
extern bool TabbedList; // True if tab list was shown

View file

@ -246,14 +246,14 @@ void PClass::StaticShutdown ()
}
FunctionPtrList.Clear();
VMFunction::DeleteAll();
// From this point onward no scripts may be called anymore because the data needed by the VM is getting deleted now.
// This flags DObject::Destroy not to call any scripted OnDestroy methods anymore.
bVMOperational = false;
// Make a full garbage collection here so that all destroyed but uncollected higher level objects
// that still exist are properly taken down before the low level data is deleted.
GC::FullGC();
// From this point onward no scripts may be called anymore because the data needed by the VM is getting deleted now.
// This flags DObject::Destroy not to call any scripted OnDestroy methods anymore.
bVMOperational = false;
Namespaces.ReleaseSymbols();

View file

@ -125,7 +125,7 @@ void FSamplerManager::SetTextureFilterMode()
{
glSamplerParameteri(mSamplers[i], GL_TEXTURE_MIN_FILTER, TexFilter[filter].minfilter);
glSamplerParameteri(mSamplers[i], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);
glSamplerParameterf(mSamplers[i], GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_texture_filter_anisotropic);
glSamplerParameterf(mSamplers[i], GL_TEXTURE_MAX_ANISOTROPY_EXT, filter > 0? gl_texture_filter_anisotropic : 1.0);
}
glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MIN_FILTER, TexFilter[filter].magfilter);
glSamplerParameteri(mSamplers[CLAMP_XY_NOMIP], GL_TEXTURE_MAG_FILTER, TexFilter[filter].magfilter);

View file

@ -81,11 +81,6 @@ CVAR(Bool, show_messages, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, show_obituaries, true, CVAR_ARCHIVE)
CCMD (toggleconsole)
{
C_ToggleConsole();
}
bool CheckCheatmode (bool printmsg, bool sponly)
{
if (sponly && netgame)

View file

@ -71,6 +71,7 @@
#include "gi.h"
#include "c_commandbuffer.h"
#define LEFTMARGIN 8
#define RIGHTMARGIN 8
@ -88,13 +89,6 @@ CUSTOM_CVAR(Int, con_buffersize, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
FConsoleBuffer *conbuffer;
static void C_TabComplete (bool goForward);
static bool C_TabCompleteList ();
static bool TabbedLast; // True if last key pressed was tab
static bool TabbedList; // True if tab list was shown
CVAR(Bool, con_notablist, false, CVAR_ARCHIVE)
static FTextureID conback;
static uint32_t conshade;
static bool conline;
@ -106,7 +100,6 @@ extern bool advancedemo;
extern FBaseCVar *CVars;
extern FConsoleCommand *Commands[FConsoleCommand::HASH_SIZE];
unsigned ConCols;
int ConWidth;
bool vidactive = false;
bool cursoron = false;
@ -116,6 +109,8 @@ constate_e ConsoleState = c_up;
double NotifyFontScale = 1;
DEFINE_GLOBAL(NotifyFontScale)
void C_SetNotifyFontScale(double scale)
{
NotifyFontScale = scale;
@ -175,344 +170,6 @@ struct History
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;
#define MAXHISTSIZE 50
static struct History *HistHead = NULL, *HistTail = NULL, *HistPos = NULL;
static int HistSize;
@ -628,7 +285,7 @@ void C_InitConsole (int width, int height, bool ingame)
cwidth = cheight = 8;
}
ConWidth = (width - LEFTMARGIN - RIGHTMARGIN);
ConCols = ConWidth / cwidth;
CmdLine.ConCols = ConWidth / cwidth;
if (conbuffer == NULL) conbuffer = new FConsoleBuffer;
}
@ -1187,20 +844,27 @@ void C_DrawConsole ()
else if (ConBottom)
{
int visheight;
FGameTexture *conpic = TexMan.GetGameTexture(conback);
visheight = ConBottom;
DrawTexture(twod, conpic, 0, visheight - twod->GetHeight(),
DTA_DestWidth, twod->GetWidth(),
DTA_DestHeight, twod->GetHeight(),
DTA_ColorOverlay, conshade,
DTA_Alpha, (gamestate != GS_FULLCONSOLE) ? (double)con_alpha : 1.,
DTA_Masked, false,
TAG_DONE);
if (conline && visheight < twod->GetHeight())
if (conback.isValid())
{
ClearRect(twod, 0, visheight, twod->GetWidth(), visheight+1, 0, 0);
DrawTexture (twod, TexMan.GetGameTexture(conback), 0, visheight - screen->GetHeight(),
DTA_DestWidth, twod->GetWidth(),
DTA_DestHeight, twod->GetHeight(),
DTA_ColorOverlay, conshade,
DTA_Alpha, (gamestate != GS_FULLCONSOLE) ? (double)con_alpha : 1.,
DTA_Masked, false,
TAG_DONE);
}
else
{
PalEntry pe((uint8_t)(con_alpha * 255), 0, 0, 0);
twod->AddColorOnlyQuad(0, 0, screen->GetWidth(), visheight, pe);
}
if (conline && visheight < screen->GetHeight())
{
twod->AddColorOnlyQuad(0, visheight, screen->GetWidth(), 1, 0xff000000);
}
if (ConBottom >= 12)
@ -1765,6 +1429,11 @@ CCMD (echo)
}
}
CCMD(toggleconsole)
{
C_ToggleConsole();
}
/* Printing in the middle of the screen */
CVAR(Float, con_midtime, 3.f, CVAR_ARCHIVE)
@ -1800,276 +1469,3 @@ void C_MidPrint (FFont *font, const char *msg, bool bold)
}
}
/****** Tab completion code ******/
struct TabData
{
int UseCount;
FName TabName;
TabData()
: UseCount(0), TabName(NAME_None)
{
}
TabData(const char *name)
: UseCount(1), TabName(name)
{
}
TabData(const TabData &other) = default;
};
static TArray<TabData> TabCommands (TArray<TabData>::NoInit);
static int TabPos; // Last TabCommand tabbed to
static int TabStart; // First char in CmdLine to use for tab completion
static int TabSize; // Size of tab string
static bool FindTabCommand (const char *name, int *stoppos, int len)
{
FName aname(name);
unsigned int i;
int cval = 1;
for (i = 0; i < TabCommands.Size(); i++)
{
if (TabCommands[i].TabName == aname)
{
*stoppos = i;
return true;
}
cval = strnicmp (TabCommands[i].TabName.GetChars(), name, len);
if (cval >= 0)
break;
}
*stoppos = i;
return (cval == 0);
}
void C_AddTabCommand (const char *name)
{
int pos;
if (FindTabCommand (name, &pos, INT_MAX))
{
TabCommands[pos].UseCount++;
}
else
{
TabData tab(name);
TabCommands.Insert (pos, tab);
}
}
void C_RemoveTabCommand (const char *name)
{
if (TabCommands.Size() == 0)
{
// There are no tab commands that can be removed.
// This is important to skip construction of aname
// in case the NameManager has already been destroyed.
return;
}
FName aname(name, true);
if (aname == NAME_None)
{
return;
}
for (unsigned int i = 0; i < TabCommands.Size(); ++i)
{
if (TabCommands[i].TabName == aname)
{
if (--TabCommands[i].UseCount == 0)
{
TabCommands.Delete(i);
}
break;
}
}
}
void C_ClearTabCommands ()
{
TabCommands.Clear();
}
static int FindDiffPoint (FName name1, const char *str2)
{
const char *str1 = name1.GetChars();
int i;
for (i = 0; tolower(str1[i]) == tolower(str2[i]); i++)
if (str1[i] == 0 || str2[i] == 0)
break;
return i;
}
static void C_TabComplete (bool goForward)
{
unsigned i;
int diffpoint;
auto CmdLineText = CmdLine.GetText();
if (!TabbedLast)
{
bool cancomplete;
// Skip any spaces at beginning of command line
for (i = 0; i < CmdLineText.Len(); ++i)
{
if (CmdLineText[i] != ' ')
break;
}
if (i == CmdLineText.Len())
{ // Line was nothing but spaces
return;
}
TabStart = i;
TabSize = (int)CmdLineText.Len() - TabStart;
if (!FindTabCommand(&CmdLineText[TabStart], &TabPos, TabSize))
return; // No initial matches
// Show a list of possible completions, if more than one.
if (TabbedList || con_notablist)
{
cancomplete = true;
}
else
{
cancomplete = C_TabCompleteList ();
TabbedList = true;
}
if (goForward)
{ // Position just before the list of completions so that when TabPos
// gets advanced below, it will be at the first one.
--TabPos;
}
else
{ // Find the last matching tab, then go one past it.
while (++TabPos < (int)TabCommands.Size())
{
if (FindDiffPoint(TabCommands[TabPos].TabName, &CmdLineText[TabStart]) < TabSize)
{
break;
}
}
}
TabbedLast = true;
if (!cancomplete)
{
return;
}
}
if ((goForward && ++TabPos == (int)TabCommands.Size()) ||
(!goForward && --TabPos < 0))
{
TabbedLast = false;
CmdLineText.Truncate(TabSize);
}
else
{
diffpoint = FindDiffPoint(TabCommands[TabPos].TabName, &CmdLineText[TabStart]);
if (diffpoint < TabSize)
{
// No more matches
TabbedLast = false;
CmdLineText.Truncate(TabSize - TabStart);
}
else
{
CmdLineText.Truncate(TabStart);
CmdLineText << TabCommands[TabPos].TabName.GetChars() << ' ';
}
}
CmdLine.SetString(CmdLineText);
CmdLine.MakeStartPosGood();
}
static bool C_TabCompleteList ()
{
int nummatches, i;
size_t maxwidth;
int commonsize = INT_MAX;
nummatches = 0;
maxwidth = 0;
auto CmdLineText = CmdLine.GetText();
for (i = TabPos; i < (int)TabCommands.Size(); ++i)
{
if (FindDiffPoint (TabCommands[i].TabName, &CmdLineText[TabStart]) < TabSize)
{
break;
}
else
{
if (i > TabPos)
{
// This keeps track of the longest common prefix for all the possible
// completions, so we can fill in part of the command for the user if
// the longest common prefix is longer than what the user already typed.
int diffpt = FindDiffPoint (TabCommands[i-1].TabName, TabCommands[i].TabName.GetChars());
if (diffpt < commonsize)
{
commonsize = diffpt;
}
}
nummatches++;
maxwidth = std::max (maxwidth, strlen (TabCommands[i].TabName.GetChars()));
}
}
if (nummatches > 1)
{
size_t x = 0;
maxwidth += 3;
Printf (TEXTCOLOR_BLUE "Completions for %s:\n", CmdLineText.GetChars());
for (i = TabPos; nummatches > 0; ++i, --nummatches)
{
// [Dusk] Print console commands blue, CVars green, aliases red.
const char* colorcode = "";
FConsoleCommand* ccmd;
if (FindCVar (TabCommands[i].TabName.GetChars(), NULL))
colorcode = TEXTCOLOR_GREEN;
else if ((ccmd = FConsoleCommand::FindByName (TabCommands[i].TabName.GetChars())) != NULL)
{
if (ccmd->IsAlias())
colorcode = TEXTCOLOR_RED;
else
colorcode = TEXTCOLOR_LIGHTBLUE;
}
Printf ("%s%-*s", colorcode, int(maxwidth), TabCommands[i].TabName.GetChars());
x += maxwidth;
if (x > ConCols / active_con_scale(twod) - maxwidth)
{
x = 0;
Printf ("\n");
}
}
if (x != 0)
{
Printf ("\n");
}
// Fill in the longest common prefix, if it's longer than what was typed.
if (TabSize != commonsize)
{
TabSize = commonsize;
CmdLineText.Truncate(TabStart);
CmdLineText.AppendCStrPart(TabCommands[TabPos].TabName.GetChars(), commonsize);
CmdLine.SetString(CmdLineText);
}
return false;
}
return true;
}

View file

@ -178,6 +178,7 @@ struct _ native // These are the global variables, the struct is only here to av
native readonly int GameTicRate;
native MenuDelegateBase menuDelegate;
native readonly int consoleplayer;
native readonly double NotifyFontScale;
}
struct MusPlayingInfo native

View file

@ -115,12 +115,17 @@ class LoadSaveMenu : ListMenu
override void Init(Menu parent, ListMenuDescriptor desc)
{
Super.Init(parent, desc);
manager = SavegameManager.GetManager();
manager.ReadSaveStrings();
SetWindows();
}
private void SetWindows()
{
bool aspect43 = true;
int Width43 = screen.GetHeight() * 4 / 3;
int Left43 = (screen.GetWidth() - Width43) / 2;
manager = SavegameManager.GetManager();
manager.ReadSaveStrings();
double wScale = Width43 / 640.;
savepicLeft = Left43 + int(20 * wScale);
@ -186,6 +191,7 @@ class LoadSaveMenu : ListMenu
return;
}
SetWindows();
DrawFrame(savepicLeft, savepicTop, savepicWidth, savepicHeight);
if (!manager.DrawSavePic(savepicLeft, savepicTop, savepicWidth, savepicHeight))
{
@ -196,9 +202,9 @@ class LoadSaveMenu : ListMenu
if (Selected >= manager.SavegameCount()) Selected = 0;
String text = (Selected == -1 || !manager.GetSavegame(Selected).bOldVersion)? Stringtable.Localize("$MNU_NOPICTURE") : Stringtable.Localize("$MNU_DIFFVERSION");
int textlen = NewSmallFont.StringWidth(text);
screen.DrawText (NewSmallFont, Font.CR_GOLD, (savepicLeft+(savepicWidth-textlen)/2) / FontScale,
(savepicTop+(savepicHeight-rowHeight)/2) / FontScale, text, DTA_VirtualWidthF, screen.GetWidth() / FontScale, DTA_VirtualHeightF, screen.GetHeight() / FontScale);
screen.DrawText (NewSmallFont, Font.CR_GOLD, (savepicLeft + savepicWidth / 2) / FontScale - textlen/2,
(savepicTop+(savepicHeight-rowHeight)/2) / FontScale, text, DTA_VirtualWidthF, screen.GetWidth() / FontScale, DTA_VirtualHeightF, screen.GetHeight() / FontScale, DTA_KeepRatio, true);
}
}

View file

@ -89,7 +89,7 @@ class MessageBoxMenu : Menu
int mr2 = destWidth/2 + 10 + textFont.StringWidth(Stringtable.Localize("$TXT_NO"));
mMouseRight = MAX(mr1, mr2);
mParentMenu = parent;
mMessage = textFont.BreakLines(Stringtable.Localize(message), generic_ui? 600 : 300);
mMessage = textFont.BreakLines(Stringtable.Localize(message), 300/NotifyFontScale);
mMessageMode = messagemode;
if (playsound)
{
@ -107,7 +107,7 @@ class MessageBoxMenu : Menu
override void Drawer ()
{
int i, y;
int fontheight = textFont.GetHeight();
int fontheight = textFont.GetHeight() * NotifyFontScale;
y = destHeight / 2;
@ -116,7 +116,8 @@ class MessageBoxMenu : Menu
for (i = 0; i < c; i++)
{
screen.DrawText (textFont, Font.CR_UNTRANSLATED, destWidth/2 - mMessage.StringWidth(i)/2, y, mMessage.StringAt(i), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true);
screen.DrawText (textFont, Font.CR_UNTRANSLATED, destWidth/2 - mMessage.StringWidth(i)*NotifyFontScale/2, y, mMessage.StringAt(i), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true,
DTA_ScaleX, NotifyFontScale, DTA_ScaleY, NotifyFontScale);
y += fontheight;
}
@ -124,8 +125,8 @@ class MessageBoxMenu : Menu
{
y += fontheight;
mMouseY = y;
screen.DrawText(textFont, messageSelection == 0? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, destWidth / 2, y, Stringtable.Localize("$TXT_YES"), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true);
screen.DrawText(textFont, messageSelection == 1? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, destWidth / 2, y + fontheight, Stringtable.Localize("$TXT_NO"), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true);
screen.DrawText(textFont, messageSelection == 0? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, destWidth / 2, y, Stringtable.Localize("$TXT_YES"), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true, DTA_ScaleX, NotifyFontScale, DTA_ScaleY, NotifyFontScale);
screen.DrawText(textFont, messageSelection == 1? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, destWidth / 2, y + fontheight, Stringtable.Localize("$TXT_NO"), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true, DTA_ScaleX, NotifyFontScale, DTA_ScaleY, NotifyFontScale);
if (messageSelection >= 0)
{