65.9K
CodeProject 正在变化。 阅读更多。
Home

JLib - Windows 控制台库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.64/5 (20投票s)

2008 年 4 月 2 日

GPL3

15分钟阅读

viewsIcon

117129

downloadIcon

2353

一个能够进行彩色输入和输出的控制台库。包含用户可定义的菜单、ASCII 字符窗口、保存/恢复整个屏幕字符、256 种控制台颜色组合以及智能着色功能。

jlib_test_scn.jpg

引言

JLib 库封装了负责控制台文本格式化的 Win32 API 函数。这些功能包括:

  • 输出格式化
  • 水平和垂直文本换行
  • MRU 光标位置和格式化的记忆
  • 从键盘获取输入
  • 用户自定义菜单,包括标准、窗口式、滚动式和窗口式滚动式
  • 256 色调色板(背景 4 位,前景 4 位)
  • 字符框
  • 带标题的窗口式框
  • 保存/恢复整个屏幕的字符和格式
  • 保存/恢复屏幕的一部分字符和格式

范围

本文档仅介绍 JLib 库函数的使用。它只描述其提供的功能和如何使用它们。它不解释该库使用的 Windows 函数的底层原理。

背景

五年前,当我开始学习软件工程时,我曾被同学在 Windows XP 的控制台应用程序中添加彩色格式化文本输出的能力所启发。最终我学会了如何自己操作,但处理所有细节变得很繁琐。因此,我创建了一个函数库,它不仅仅是将格式化文本数组简单地显示在屏幕上。

当时,我正在学习 C、C++ 和 MFC 的用法。我非常喜欢(现在依然如此)MFC 库,所以我编写了一些代码来模仿 MFC 代码在 MSDN 文档中的显示方式。

四年后,我重新审视了最初的 JLib。自从我最初编写它以来,我的开发能力已经有了长足的进步,而且我为它当初的粗糙感到羞愧。我决定对其进行一次大修,并最终彻底修复智能色彩格式化功能。

为什么它被命名为 JLib?因为是我编写的这个库,而我的名字以字母 J 开头。

Using the Code

此版本的 JLib 可以使用 Microsoft Visual Studio .NET 2010 进行编译。也可以在任何较早版本的 Microsoft Visual Studio 中编译,最远可追溯到 6.0。您需要将诸如 _itoa_s_kbhit_getch 等安全函数替换为其已弃用的版本。

该库由 ConsoleCore 类组成,该类封装了 windows.h 头文件中的控制台格式化调用。ConsoleCore 是一个单例,通过 ConsoleCore::GetInstance 方法访问。

注意:ConsoleCore 不是线程安全的!

该库还明确使用了标准模板库来进行颜色格式化、向量和列表容器。

本文档将要涵盖的关于使用该库的主要要点是:

  • 如何将其添加到我的项目中?
  • 定义和类
  • 如何打印 string
  • 如何打印数字
  • 如何使用 ConsoleFormat
  • 如何格式化输出
  • 如何从键盘读取 string
  • 如何从键盘读取数字
  • 如何绘制字符框
  • 如何绘制带/不带标题的字符窗口框
  • 如何创建菜单
  • 如何使用菜单
  • 如何保存/加载/清除屏幕的某些部分或全部内容

如何添加到我的项目中?

将源代码放在项目文件夹的相应文件夹中,然后包含头文件

#include "ConsoleCore.h"

定义和类

一切都始于控制台定义

#define MAXSCREENX			(80)						// Standard sized window dimensions
#define MAXSCREENY			(25)
#define SCREEN_BUFFER_SIZE	(MAXSCREENX * MAXSCREENY)
#define BADMENU				(60000)
#define USERESC				(60001)
#define UP_KEY				(72)
#define DOWN_KEY			(80)
#define RETURN				(13)
#define ESCAPE				(27)
#define KB_EXTENDED			(224)						// Returned from kbhit if an extended key is pressed

JLib 基于 80x25 字符的控制台。菜单功能操作方向键、回车键和 Esc 键。JLib 还使用 _kbhit 来捕获菜单或提示处于活动状态时的用户输入。

ConsoleCore 负责屏幕输出和键盘输入。它本身提供了基本的键盘和屏幕 I/O 功能,但允许您控制光标的格式化和位置。

JLib 库附带了一些类,这些类使用 ConsoleCore 来处理一些更有趣和更繁琐的任务。

//		ConsoleFormat
//	Contains a bitset of 8 bits that represents the coloring used
//	when writing to the screen.  The bits for coloring, and their
//	order, can be found in the Bit enumeration.  Also provides
//	some predefined colors that are common.
class ConsoleFormat;

//		CharacterBox
//	A class that uses the ConsoleCore to render an ASCII box to the screen.
//  You can specify what color formatting to use for the border and the client.
//  You can set the character that is used to draw the outline of the box.
class CharacterBox;

//		CharacterWindow
//	A CharacterBox with a title.  It has the same properties as CharacterBox
//	but it is drawn differently.  The title portion is encased in its own
//	box.  The title is formated with the client format.
//	You must make sure to make the dimensions of the box large enough
//	to encase the title or the title can overrun the box.
class CharacterWindow : public CharacterBox;

//		ConsoleMenuItem
//	This represents an item in a menu.  Menu items have a text label
// and a userdefined value.  The user defined value is of type DWORD
// so you could potentially store something more meaningful than a 
// number, like a function address.
class ConsoleMenuItem;

//		ConsoleMenu
//	A ConsoleMenu is a collection of items.  Menus can draw themselves
//	and use ConsoleCore.  To launch a menu call Show and then check
//	the return value.  To retreive information from a selected menu item
//	after calling show, call one of the Selected functions.
//	Use the up and down arrow keys to change your selection.  Press
//	enter to make a selection.  Press escape to dismiss the menu.
//	This menu also supports wrapping from top to bottom.
//	You can also set the coloring for menu items.  The regular format
//	is the color used to draw items that are not selected.  The
//	selection format is used on the item that is currently highlighted.
class ConsoleMenu;

//		ScrollingMenu
//	The scrollingMenu functions like the ConsoleMenu except
//	it has the ability to hide some of its items.  This makes the
//	ScrollingMenu ideal when screen real estate is limited.
//	As the user presses the up and down arrows the menu items
//  in "view" will shift automatically.
class ScrollingMenu : public ConsoleMenu;

//		WindowedMenu
//	The windowed menu combines the abilities of a CharacterWindow
//	and a ScrollingMenu.  It will automatically draw a CharacterWindow
//	around itself when shown.
//	WindowedMenus do not have to be scrollable.  You can enable or
//	disable this with the Scrollable function.  When Scrolling is disabled
//	the menu will act like a ConsoleMenu.  When SCrolling is enabled
//	the menu will act like a ScrollingMenu and the size of the CharacterWindow
//	will fit the maxToShow number of items.
//	WindowedMenus have two sets of color formatting.  Format and selectionFormat
//	apply to the items displayed.  WindowColor and clientColor apply to the
//	windowed box.  The title of the window is drawn using the clientColor.
class WindowedMenu : public ScrollingMenu;

如何打印字符串

要打印 string,您需要使用 ConsoleCore::Print 方法之一。

签名如下:

//		Prints
//	Writes a string to the screen and updates the cursor position.
//	Arguments:
//		string text:	The string to write.  Can contain embedded color codes.
//		BOOL endline:	Whether to move the cursor down and all the way left after writing.
//		ConsoleFormat* color:	The color to use.
//		SHORT x:	Column to write to.
//		SHORT y:	Row to write to.
//	Notes:
//		If color is NULL, the default color is used.
//		If x or y are -1 the current x and/or y value is used.
//		Text can contain color codes to format portions of the screen.
//		Color codes are always in the format of a dollar sign followed by exactly 3 digits (i.e. $007)
//		All text after a color code will be written to the screen in that color until another color
//		code is encountered.
//		Color codes supersede the current default color but do not modify it.
void Prints(string text, BOOL endLine = FALSE, const ConsoleFormat* color = NULL, SHORT x = -1, SHORT y = -1);

Example usage

ConsoleCore::GetInstance()->Prints("Hello World!",TRUE,NULL,0,0);
ConsoleCore::GetInstance()->Prints("Console output is fun!");

上面的代码将输出...

Hello World!
Console output is fun!

...控制台光标将停留在第二个感叹号之后。

如何打印数字

打印数字与打印 string 相同,只是您传递的是数字。

//		Printn
//	Writes a number to the screen and updates the cursor position.
//	Arguments:
//		DWORD number:	The value to write.
//		BOOL endline:	Whether to move the cursor down and all the way left after writing.
//		ConsoleFormat* color:	The color to use.
//		SHORT x:	Column to write to.
//		SHORT y:	Row to write to.
//	Notes:
//		If color is NULL, the default color is used.
//		If x or y are -1 the current x and/or y value is used.
void Printn(DWORD number, BOOL endLine = FALSE, ConsoleFormat* color = NULL, SHORT x = -1, SHORT y = -1);

该库还提供了一个用于打印双精度浮点数的函数。同样,它类似于打印 string 和整数,但您在使用它时必须格外小心。它使用 _gcvt_sdouble 转换为 string。但是,如果需要,该函数也会在字符串转换的末尾附加指数符号。签名如下,digits 参数的含义在后面进一步解释。

//		Printd
//	Writes a double to the screen.
//	Arguments:
//		DOUBLE number:	The value to write.
//		int characterLength:	The length of the output.
//		BOOL endline:	Whether to move the cursor down and all the way left after writing.
//		ConsoleFormat* color:	The color to use.
//		SHORT x:	Column to write to.
//		SHORT y:	Row to write to.
//	Notes:
//		If color is NULL, the default color is used.
//		If character length is shorter than what it would take to display the entire double 
//		then the value will be displayed in scientific notation. i.e 9e-004 (or 0.0009).
//		If x or y are -1 the current x and/or y value is used.
void Printd(DOUBLE number, int characterLength, BOOL endLine = FALSE, ConsoleFormat* color = NULL, SHORT x = -1, SHORT y = -1);

浮点单元可能很微妙。有时精度更高,有时则不然。doublestring 的转换可能会或可能不会根据您给定的值附加指数。这里有一些例子及其输出,请注意,在最后一个例子中,digits4,但转换后的 string 的实际长度更长。

ConsoleCore::GetInstance()->Printd(2.3449L,2); // "2.3"
ConsoleCore::GetInstance()->Printd(2.39L,2); // "2.4"
ConsoleCore::GetInstance()->Printd(1.001L,4); // "1.001"
ConsoleCore::GetInstance()->Printd(12000L,4); // "1.2e+4

如果您打算处理大数字,请考虑输出可能比数字本身宽 5 个字符。根据 MSDN 2005 文档,double 的值范围从 2.2250738585072014 E – 308 到 1.7976931348623158 E + 308。要打印这些极端值之一,您需要将 digits 指定为至少 20,但仍需要 25 个字符才能打印出附加了指数的值……然后,您总是可以将数字转换为 string,然后在打印之前根据需要进行格式化,再使用 Prints()

如何使用 ConsoleFormat

ConsoleFormat 封装了输出上下文中的数据。技术上讲,4 位用于前景颜色,即显示的字符,4 位用于背景,共 8 位。两者最后 3 位代表红、绿、蓝分量,第 1 位代表 Alpha 或强度(亮度)。

有几种方法可以操作 ConsoleFormat 对象:
最简单的方法是基于预定义常用颜色与 ConsoleFormat::Colors enum 的按位 OR 来创建一个格式。您可以在构造时这样做,也可以通过 ConsoleFormat::Color() 修改器来完成。

ConsoleFormat ccf = CONSOLECHARFORMAT::BRIGHTBLUE | CONSOLECHARFORMAT::ONYELLOW;
ConsoleFormat ccf2(CONSOLECHARFORMAT::BRIGHTBLUE | CONSOLECHARFORMAT::ONYELLOW);

这将创建一个文本颜色为亮蓝色、背景颜色为黄色的格式。您还可以按位 OR ConsoleFormat 对象,或调用一元反转方法来获取逻辑上相反的颜色。

有时您想要一种无法通过 enum 中的预定义颜色创建的颜色。ConsoleFormat 提供了一些辅助工具,可以帮助您在位集中设置特定的颜色位,而无需了解 STL。ConsoleFormat 包含允许您 getset 每个位的值的方法。设置位的参数化方法是 void ConsoleFormat::Set(Bit bit, bool value)。值将被赋给相应的位,除非您使用了 ConsoleFormat::Bit 中不存在的某个值。bit 的值决定该值应用于 foreground 还是 background。此外,还有一个 bool ConsoleFormat::Get(Bit bit) 方法用于检索任何位的值。

这里有两种创建相同颜色的方法:

ConsoleFormat cf = ConsoleFormat::SYSTEM; // default console color.
ConsoleFormat cf2;
cf2.Set(ConsoleFormat::FRONT_RED,TRUE);
cf2.Set(ConsoleFormat::FRONT_GREEN,TRUE);
cf2.Set(ConsoleFormat::FRONT_BLUE,TRUE);

ConsoleFormat 还包含几种转换构造函数以及从 unsigned char 的转换。

如何格式化输出

下面是如何使用自定义 ConsoleFormatstring 或数字写入屏幕。

ConsoleFormat cfString, cfNumber;
... // set the bits to your liking
ConsoleCore::GetInstance()->Prints("Hello World!",TRUE,&cfString);
ConsoleCore::GetInstance()->Printn(1024,TRUE,&cfNumber);

有时您可能想打印一个 string,但其中的一部分颜色与其他部分不同。一种方法是创建多个 ConsoleFormat 对象,并显式地打印每个部分,如下所示:

ConsoleFormat red, green, blue;
... // set them accordingly
ConsoleCore::GetInstance()->Prints("Red ",FALSE,&red);
ConsoleCore::GetInstance()->Prints("Green ",FALSE,&green);
ConsoleCore::GetInstance()->Prints("Blue",FALSE,&blue);

这会很快变得很麻烦。我给了 ConsoleCore 解析传递给 Prints 的文本的能力,正是出于这个原因。而不是编写上面的代码,您可以这样做……

cConsoleCore::GetInstance()->Prints("$004Red $002Green $001Blue");

……并获得相同的结果。

ConsoleCore 的辅助程序会扫描您的 string 并提取所有 $### 部分。检索到的格式用于着色直到下一个 $### 格式代码的所有文本。不幸的是,这不适用于 Printn(),因为它接受 DWORD 而不是 string。代码必须正好是 3 位数字,并以 '$' 开头。

在利用此功能时,您还应注意在 string 中包含数字:

ConsoleCore::GetInstance()->Prints("I have $$002000 dollars!")

……显示“I have $000 dollars!”,其中“000 dollars!”为绿色背景黑色文本。下面的情况也可能出乎意料:

console.Prints("I have $$2000 dollars!")

……显示“I have $0 dollars!”,其中“$000 dollars!”为灰色背景红色文本。

如果意图是将“I have $2000 dollars!”写入屏幕,其中“$2000”为绿色背景黑色文本,之后的所有内容均为标准的白色背景黑色文本,那么有效的方法是:

ConsoleCore::GetInstance()->Prints("I have$002 $$0022000 $007dollars!",TRUE);

如何从键盘读取字符串

ScanString() 方法正是为此而设计的,其签名如下:

//		ScanString
//	Reads input as a string from the keyboard.
//	Arguments:
//		COORD origin:	Where the prompt is placed.
//		char* buffer:	Out parameter to collect the input.
//		UINT maxLength:	Maximum amount of characters to be entered.
//	Notes:
//		maxLength should be 1 less than the maximum capacity of
//	buffer.
void ScanString(COORD origin, char* buffer, UINT maxLength);

如何从键盘读取数字

ScanNumber() 方法正是为此而设计的。它不允许用户输入除数字或负号以外的任何字符,并且负号只能作为第一个字符输入。它还可以选择指定允许的最大数字位数。您可以指定超过 10 位数字,但 DWORD 仅需要 10 位数字即可表示其最大或最小值。

//		ScanNumber
//	Reads input as a number from the keyboard.
//	Arguments:
//		COORD origin:	Where the prompt is placed.
//		LPDOWRD lpNumber:	Out parameter to collect the input.
//		int digitCount:	Maximum number of digits allowed.
//	Notes:
//		The minus sign can be used as the first character and does not count toward the limit
//		set by digitCount.
//		The range value of a DWORD is [-2147483648,2147483647]. If you enter a digit above 
//		or below that value, say 99999999999 to -9999999999, it will be clamped 
//		automatically before given back through lpNumber.
void ScanNumber(COORD origin, LPDWORD lpNumber, int digitCount = 10);

您还可以使用此库使用 ScanDouble() 从键盘读取 double,如下所述。

//		ScanDouble
//	Reads input as a double from the keyboard
//	Arguments:
//		COORD origin: Where the prompt is placed.
//		DOUBLE* pDouble:	Out parameter to collect the input.
//	Notes:
//		Can be entered as a typical floating point number (12.345)
//	or as scientic notation (3.2e-4).
//  The range of a double, according to MSDN 2005 documentation, 
//     is 2.2250738585072014 E – 308 to 1.7976931348623158 E + 308.
// If you enter a digit outside of the range you may not get the desired result.  
// Additionally, because FPUs are fickle
// the number you enter may not always be the precisely the one 
// that is returned through the out parameter.
void ScanDouble(COORD origin, DOUBLE* pDouble);

您可以使用多种格式输入 double,例如“1.01”、“0.1”、“.1”,甚至“1243.2e+001”和“23.09e-203”。如果您要在输入中使用 E,则后面必须跟 + 或 -,否则将不允许输入指数部分。该函数不区分大小写。

如何绘制字符框

CharacterBoxCharacterWindow 是代表 ASCII 框的对象。

CharacterBox 有两个 COORD 结构,用于定义左上角(也是原点)和右下角。它还有两个 ConsoleFormat 成员,用于定义边框和客户区(内部)部分的颜色。您还可以指定用于绘制轮廓的字符。该字符是填充字符。框可以使用 Draw 方法绘制自身。CharacterBox 还包含一个静态 Draw 方法,该方法将创建一个临时框并绘制它。唯一的区别是,静态版本将始终拥有 ConsoleFormat::BLACK 的客户区,并使用 ConsoleFormat::SYSTEM 颜色作为边框。CharacterBox 实例会自动规范化其角落。这种规范化确保角落位于控制台窗口的 80x25 字符尺寸范围内。但是,此方法不能确保左上角真正位于右下角的上方和左侧。

CharacterWindow 类是 CharacterBox 的子类。它有一个额外的 string 成员用于标题,并且在绘制方式上有所不同。它创建父类的两个实例来绘制。第一个用于绘制顶部框,该框高 1 行,并在其中绘制标题。第二个框是客户区。颜色格式基本相同,只是窗口标题使用客户区格式绘制。如果您创建自己的 CharacterWindow 对象,应注意其宽度至少是标题 string 长度 + 2。否则,标题可能会绘制到框外。

这是使用静态方法绘制覆盖整个屏幕的框的方法。

COORD upperLeft,
      lowerRight;
char fill = '*';
upperLeft.X = upperLeft.Y = 0;
lowerRight.X = MAXSCREENX - 1;
lowerRight.Y = MAXSCREENY - 1;
CharacterBox::Draw(upperLeft,lowerRight,fill);

当然,您可能希望绘制一个具有特定边框和客户区格式的框,并在其中重用。

//Include code above
CharacterBox box(upperLeft,lowerRight,ConsoleFormat::BRIGHTWHITE,~ConsoleFormat::BRIGHTWHITE);
box.Draw();

如何绘制带/不带标题的字符窗口框

这是一个使用 CharacterWindow 对象的示例。请注意,指定的尺寸适用于框的整个区域,而不仅仅是客户区。

ConsoleCore* pCore = ConsoleCore::GetInstance();
char* title = "example";
COORD ul = {31,0}
	  ,br = {37,6};
ConsoleFormat client = ConsoleFormat::BRIGHTYELLOW
	,old = pCore->Color();
CharacterWindow window(ul,br,title);
window.ClientColor(client);
window.Draw();	// The first box is too skinny for the title!

ul.X = 39;
br.X = ul.X + strlen(title) + 2;
window.UpperLeft(ul);
window.LowerRight(br);
// +1 for the left side of the box
// +1 for the right side of the box
window.Draw();

输出看起来像这样:

......   .........
.example .example.
......   .........
.    .   .       .
.    .   .       .
......   .........

CharacterWindow 不会尝试居中文本标题。如果您需要这样做,可以考虑手动绘制两个框,然后再绘制标题。

如何创建菜单

控制台编程中最令人头疼的事情之一就是菜单。如果没有直接屏幕访问,您的选择相当有限。这是我们可能用来创建控制台菜单的一个例子。

char ch;
do
{
    printf("Choose an option\n");
    printf("(C)reate\n");
    printf("(M)odify\n");
    printf("(Q)uit\n");
    scanf("%c",&ch);
    ch = tolower(ch);
    switch(ch)
    {
        case 'c':
        {...}break;
        case 'm':
        {...}break;
        default: break;
    }
}
while(ch != 'q');

另外,无论您选择使用 stdio 还是 STL iostream,都必须“监视”用户,以防他们输入错误的数据。我们都知道不检查流会发生什么……多余的数据会落入下一个输入,整个应用程序就会开始失控。

JLib 使控制台菜单成为一种智能、优雅(控制台应用程序能有多优雅就有多优雅)的体验,而且使用它们非常方便。它们以用户定义的 ConsoleMenuItem 开始。ConsoleMenuItem 是一个带有值的文本标签。在创建菜单时,您需要指定原点、当前选定项目的颜色格式以及所有其他项目的颜色格式。JLib 中的所有菜单都提供多种方法来插入和追加菜单项。菜单还提供删除所有项目的能力。

总的来说,从用户的角度来看,JLib 中的菜单是防呆的。因为只使用箭头、回车和 Esc 键进行选择,所以不可能返回错误数据,除非菜单创建时没有项。用户按下向上或向下键移动到不同的菜单项,然后按回车键进行选择。另外,如果用户按下 Esc 键,将返回定义为 USERESC 的值。它为您提供了一种替代方法来检测用户是想“取消”选择还是想关闭菜单。

有三种类型的菜单:ConsoleMenuScrollingMenuWindowedMenu。最后一种类型有两种模式:可滚动和不可滚动。以下是创建每种类型的示例:

COORD origin = {31,0};
ConsoleFormat ccfRed = ConsoleFormat::BRIGHTRED
	,ccfOld;
// Create a ConsoleMenu
ConsoleMenu menu(origin);
// There are 2 other methods for appending items.
// These other methods exist for all menu types.
menu.Append("Menu Item 1",0);
menu.Append("Menu Item 2",0);
menu.Append("Menu Item 3",0);

// Create a Scrollable Menu
ScrollingMenu menu(origin,4,ccfRed,~ccfRed);
menu.Append("Menu Item 1",0);
menu.Append("Menu Item 2",1);
menu.Append("Menu Item 3",2);
menu.Append("Menu Item 4",3);
menu.Append("Menu Item 5",4);
menu.Append("Menu Item 6",5);
menu.Append("Menu Item 7",6);
menu.Append("Menu Item 8",7);

// Create a Windowed Menu that doesn't support scrolling
COORD origin = {31,0};	
WindowedMenu menu(origin,4,"Wnd Menu"); 
// The construct allows the setting of the window color and client color
// but this is to demonstrate that you can change the formatting of menus
// after creation.
menu.WindowColor(ConsoleFormat::BRIGHTBLUE);
menu.ClientColor(ConsoleFormat::ONYELLOW);
menu.Append("Menu Item 1",0);
menu.Append("Menu Item 2",1);
menu.Append("Menu Item 3",2);
menu.Append("Menu Item 4",3);
menu.Append("Menu Item 5",4);
menu.Append("Menu Item 6",5);
menu.Append("Menu Item 7",6);
menu.Append("Menu Item 8",7);
menu.Scrollable(FALSE);

// Create a Windowed Menu that supports scrolling
// ... same as Creating a windowed menu but...
menu.Scrollable(TRUE);

如何使用菜单

所有菜单都有一个名为 Show() 的方法。这是一个通用用法示例。

// Assuming you've made some menu properly and it is called menu.
switch(menu.Show())
{
	case USERESC:
		//When escape is pressed...
	break;
	case BADMENU:
		//When the menu has no items...
	break;
	default:
		ConsoleMenuItem selection = menu.SelectedItem();
		// You can access the Text() and Value() members of selection now.
	break;
}

在处理可滚动菜单时必须小心。这些类型的菜单的成员之一是可见项目的数量。如果将此值设置得大于或等于菜单中的项目数,则 Show() 的行为将是未定义的。在 WindowedMenu 的情况下,如果您已将 Scrollable(FALSE) 设置为 FALSE,则不必担心此问题。

就是这样。每种类型的菜单在绘制和行为上略有不同,但在 Show() 方法中实现了基本的多态性。另外,请利用 ConsoleMenuItem 具有用户定义值这一事实,这些值的大小为 DWORD。您可以设计一个系统,将这些值视为地址!

如何保存/加载/清除屏幕的某些部分或全部内容

ConsoleCore 有几个用于这些情况的便捷方法。您可以使用 ClearScreen() 来清除整个屏幕,它会擦除所有文本和格式。这也会将光标放回左上角。

有时,您可能会发现将屏幕的一部分或全部内容保存起来以备将来使用很有用。SaveScreen() 函数可以做到这一点,它有两个重载。其签名如下:

//		SaveScreen
//	Copies the contents of the entire screen into an internal buffer.
//  The dimensions of the buffer are defined by SCREEN_BUFFER_SIZE.
void SaveScreen();

//		SaveScreen
//	Copies a rectangular area of the screen into a user defined buffer.
//	Saving portions of the screen outside of MAX_SCREEN_X and MAX_SCREEN_Y
//	is undefined.
//	Arguments:
//		PCHAR_INFO buffer:	User defined CHAR_INFO buffer.
//		COORD bufferSize:	Dimensions of the buffer.
//		COORD saveOrigin:	The upper left corner of the area to copy from the screen.
void SaveScreen(PCHAR_INFO buffer, COORD bufferSize, COORD saveOrigin);

以下是关于第二个签名的重要注意事项。您负责为 buffer 分配适当的内存以及销毁它。将其分配为一个一维数组,其中包含足够的元素来覆盖您要捕获区域的宽度和高度。低级函数将其视为二维数组,您必须提供高度和宽度。如果您想知道为什么 bufferSize 是一个 COORD 结构而不是 SIZE 结构,那么这就是低级 API 函数需要的参数,我不会与之抗争。

一旦您保存了屏幕或其一部分,就可以使用 LoadScreen() 将它们加载回屏幕,它也有两个与相关保存函数类似的重载。

//		LoadScreen
//	Copies the internal buffer to the screen.
void LoadScreen();

//		LoadScreen
//	Copies a user defined buffer to the screen
//	Arguments:
//		PCHAR_INFO buffer:	User defined CHAR_INFO buffer.
//		COORD bufferSize:	Dimensions of the buffer.
//		COORD loadOrigin:	The upper left corner of the screen to begin the copy.
void LoadScreen(PCHAR_INFO buffer, COORD bufferSize, COORD loadOrigin);

保存和加载屏幕是提供的菜单内部使用的,以防止在滚动菜单时发生的一些闪烁。它们可能有用,也可能无用,但它们是为那些能想到其他用途的人提供的。有关保存和加载屏幕一部分的示例,请参阅源代码中的 testharness.cpp 中的 CustomBuffer() 函数。

历史

  • 2008 年 4 月 1 日
    • 首次发布
  • 2008 年 4 月 3 日
    • 修复了演示应用程序的屏幕位置问题
  • 2008 年 4 月 4 日
    • 菜单
      • 使用菜单不再修改 SaveScreen()LoadScreen() 无参数版本使用的内部缓冲区
      • 实现了屏幕部分内容的保存/加载,这大大减少了菜单选择期间的闪烁
      • 删除了 WindowedMenuBoxWindowedScrollMenuBox 中的冗余
    • 输入
      • 改进了 ScanNumber。它不再接受无效字符,并且可以限制为只允许输入指定数量的数字。
      • 现在可以使用 ScanDouble() 从键盘读取 Double
    • 输出
      • 现在可以使用 Printd()Double 写入屏幕
  • 2008 年 4 月 14 日
    • 输出
      • Prints() 现在在包含 $### 智能着色时能够正确工作,无论字符串是否为 const
  • 2011 年 8 月 5 日
    • 输出
      • 彻底修复了 $### 格式化问题。现在它应该 100% 有效。
      • 销毁了 JLib。将其分解为更小的部分,并将工作量分配给其他对象。
      • ConsoleCore 现在负责写入简单数据和从键盘读取输入。
      • ConsoleFormat 取代了 CONSOLECHARFORMAT
      • 添加了 ConsoleMenuItemConsoleMenuScrollingMenuWindowedMenu
      • 添加了 CharacterBoxCharacterWindow
      • ConsoleCore 不再负责创建或销毁这些新对象。
© . All rights reserved.