Windows C++控制台菜单库
声明:
开发环境为 VS2022 C++,菜单库源码(2200行左右)为本人独立制作,转载使用请标明出处,谢谢!
演示视频:
C++菜单库例子展示
一、前言
做控制台菜单的时候总感觉太耗时了,因为坐标不对需要修改,还要一个一个检查,以及颜色的处理。
具体操作可看这篇博客C语言伪图形与键盘操作加扫雷实例(写的太挫了),所以我想制作一个控制台菜单库,便于在菜单方面的快速开发。
二、具体框架
因为制作的时候是走一步看一步,导致设计十分冗余,后续有时间可能会更新调整。这里我列举一些里面的功能:
- set 包含常用的函数,例如光标移动、隐藏等等。
- menu(模板) 包含选项、框架、内置键盘操作、内置鼠标操作、打印、清除等等。
- text(模板) 包含打印、清除等等。
在使用宽字符(wchar_t)对应的类,如:wmenu、wtext、应该加上这一句:
cpp
std::wcout.imbue(std::locale("zh_CN"));
详细可参考 C++基础(十八)区域设置、locale、中文乱码、中文不输出 ,这里不赘述。
三、源码展示
console_screen_set.h
cpp
#pragma once
#include <CoreWindow.h>
#include <stdio.h>
namespace my
{
inline void SetPos(int x, int y)
{
COORD pos = { x, y };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}
inline void HideCursor()
{
CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
inline void SetWordSize(int SizeX, int SizeY)
{
CONSOLE_FONT_INFOEX font_infoex;
COORD pos = { SizeX, SizeY };
font_infoex.cbSize = sizeof(CONSOLE_FONT_INFOEX);
font_infoex.dwFontSize = pos;
SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), false, &font_infoex);
}
inline void FlushBuffer()
{
FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
}
inline void SetScreenSize(short consoleWide, short consoleHigh)
{
char cmd[100];
sprintf_s(cmd, "mode con cols=%d lines=%d", consoleWide, consoleHigh);
system(cmd);
}
inline void GetMouseOperate()
{
if (!SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_EXTENDED_FLAGS | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT))
{
fprintf(stderr, "%s\n", "SetConsoleMode");
}
}
}
frame
console_screen_frame_base.h
cpp
#pragma once
#include <CoreWindow.h>
#include <cassert>
namespace my
{
template<class T>
class console_screen_frame_base
{
template<class Type, class CH, class STRING>
friend class console_screen_frame;
template<class PFUNC, class MENU, class FRAME, class CHAR, class STRING>
friend class console_screen_menu;
template<class TEXTCHAR, class FRAME, class STRING, class CHAR>
friend class console_screen_text;
public:
console_screen_frame_base(int x = 0, int y = 0, int high = 5, int wide = 10,
T up = 0, T down = 0, T left = 0, T right = 0,
T top_left = 0, T bottom_left = 0, T top_right = 0, T bottom_right = 0)
:_x(x)
, _y(y)
, _high(high)
, _wide(wide)
, _up(up)
, _down(down)
, _left(left)
, _right(right)
, _top_left(top_left)
, _bottom_left(bottom_left)
, _top_right(top_right)
, _bottom_right(bottom_right)
{
assert(x >= 0 && y >= 0);
assert(high >= 0 && wide >= 0);
}
void changePos(int x, int y)
{
assert(x >= 0 && y >= 0);
_x = x;
_y = y;
}
void changeFrameSize(int high, int wide)
{
assert(high >= 0 && wide >= 0);
_high = high;
_wide = wide;
}
void changeSide(T up = 0, T down = 0, T left = 0, T right = 0)
{
_up = up == 0 ? _up : up;
_down = down == 0 ? _down : down;
_left = left == 0 ? _left : left;
_right = right == 0 ? _right : right;
}
void changeCorner(T top_left = 0, T bottom_left = 0, T top_right = 0, T bottom_right = 0)
{
_top_left = top_left == 0 ? _top_left : top_left;
_bottom_left = bottom_left == 0 ? _bottom_left : bottom_left;
_top_right = top_right == 0 ? _top_right : top_right;
_bottom_right = bottom_right == 0 ? _bottom_right : bottom_right;
}
void changeUp(T up)
{
_up = up;
}
void changeDown(T down)
{
_down = down;
}
void changeLeft(T left)
{
_left = left;
}
void changeRight(T right)
{
_right = right;
}
void changeTopLeft(T top_left)
{
_top_left = top_left;
}
void changeTopRight(T top_right)
{
_top_right = top_right;
}
void changeBottomLeft(T bottom_left)
{
_bottom_left = bottom_left;
}
void changeBottomRight(T bottom_right)
{
_bottom_right = bottom_right;
}
protected:
void printFrame()
{
if (_up != 0)
{
printCharUp();
}
if (_down != 0)
{
printCharDown();
}
if (_left != 0)
{
printCharLeft();
}
if (_right != 0)
{
printCharRight();
}
if (_top_left != 0 ||
_top_right != 0 ||
_bottom_left != 0 ||
_bottom_right != 0)
{
printCharCorner();
}
}
virtual void printCharUp() = 0;
virtual void printCharDown() = 0;
virtual void printCharLeft() = 0;
virtual void printCharRight() = 0;
virtual void printCharCorner() = 0;
void cleanFrame()
{
if (_up != 0)
{
cleanCharUp();
}
if (_down != 0)
{
cleanCharDown();
}
if (_left != 0)
{
cleanCharLeft();
}
if (_right != 0)
{
cleanCharRight();
}
if (_top_left != 0 ||
_top_right != 0 ||
_bottom_left != 0 ||
_bottom_right != 0)
{
cleanCharCorner();
}
}
virtual void cleanCharUp() = 0;
virtual void cleanCharDown() = 0;
virtual void cleanCharLeft() = 0;
virtual void cleanCharRight() = 0;
virtual void cleanCharCorner() = 0;
protected:
short _x;
short _y;
short _high;
short _wide;
T _up;
T _down;
T _left;
T _right;
T _top_left = 0;
T _bottom_left = 0;
T _top_right = 0;
T _bottom_right = 0;
};
}
console_screen_frame_char.h
cpp
#pragma once
#include "console_screen_frame_base.h"
#include "console_screen_set.h"
#include <iostream>
namespace my
{
extern void SetPos(int x, int y);
class console_screen_frame_char : public console_screen_frame_base<signed char>
{
public:
console_screen_frame_char(int x, int y, int high, int wide,
signed char up = 0, signed char down = 0,
signed char left = 0, signed char right = 0,
signed char top_left = 0, signed char bottom_left = 0,
signed char top_right = 0, signed char bottom_right = 0)
:console_screen_frame_base(x, y, high, wide, up, down, left, right,
top_left, bottom_left, top_right, bottom_right)
{
;
}
void show()
{
printFrame();
}
void clean()
{
cleanFrame();
}
protected:
void printCharUp()
{
SetPos(_x + 1, _y);
for (int wide = 1; wide <= _wide - 2; ++wide)
{
std::cout << _up;
}
}
void printCharDown()
{
SetPos(_x + 1, _y + _high - 1);
for (int wide = 1; wide <= _wide - 2; ++wide)
{
std::cout << _down;
}
}
void printCharLeft()
{
for (int high = 1; high <= _high - 2; ++high)
{
SetPos(_x, _y + high);
std::cout << _left;
}
}
void printCharRight()
{
for (int high = 1; high <= _high - 2; ++high)
{
SetPos(_x + _wide - 1, _y + high);
std::cout << _right;
}
}
void printCharCorner()
{
SetPos(_x, _y);
std::cout << _top_left;
SetPos(_x + _wide - 1, _y);
std::cout << _top_right;
SetPos(_x, _y + _high - 1);
std::cout << _bottom_left;
SetPos(_x + _wide - 1, _y + _high - 1);
std::cout << _bottom_right;
}
void cleanCharUp()
{
signed char temp = _up;
_up = ' ';
printCharUp();
_up = temp;
}
void cleanCharDown()
{
signed char temp = _down;
_down = ' ';
printCharDown();
_down = temp;
}
void cleanCharLeft()
{
signed char temp = _left;
_left = ' ';
printCharLeft();
_left = temp;
}
void cleanCharRight()
{
signed char temp = _right;
_right = ' ';
printCharRight();
_right = temp;
}
void cleanCharCorner()
{
signed char temp1 = _top_left;
signed char temp2 = _bottom_left;
signed char temp3 = _top_right;
signed char temp4 = _bottom_right;
changeCorner(' ', ' ', ' ', ' ');
printCharCorner();
changeCorner(temp1, temp2, temp3, temp4);
}
};
}
console_screen_frame_wchar_t.h
cpp
#pragma once
#include "console_screen_frame_base.h"
#include "console_screen_set.h"
#include <iostream>
namespace my
{
class console_screen_frame_wchar_t : public console_screen_frame_base<wchar_t>
{
public:
console_screen_frame_wchar_t(int x, int y, int high, int wide,
wchar_t up = 0, wchar_t down = 0,
wchar_t left = 0, wchar_t right = 0,
wchar_t top_left = 0, wchar_t bottom_left = 0,
wchar_t top_right = 0, wchar_t bottom_right = 0)
:console_screen_frame_base(x, y, high, wide, up, down, left, right,
top_left, bottom_left, top_right, bottom_right)
{
;
}
void show()
{
printFrame();
}
void clean()
{
cleanFrame();
}
protected:
void printCharUp()
{
for (int wide = 0; wide <= _wide - 6; wide += 2)
{
SetPos(_x + 2 + wide, _y);
std::wcout << _up;
}
}
void printCharDown()
{
for (int wide = 0; wide <= _wide - 6; wide += 2)
{
SetPos(_x + 2 + wide, _y + _high - 1);
std::wcout << _down;
}
}
void printCharLeft()
{
for (int high = 1; high <= _high - 2; ++high)
{
SetPos(_x, _y + high);
std::wcout << _left;
}
}
void printCharRight()
{
for (int high = 1; high <= _high - 2; ++high)
{
SetPos(_x + _wide - 2, _y + high);
std::wcout << _right;
}
}
void printCharCorner()
{
SetPos(_x, _y);
std::wcout << _top_left;
SetPos(_x + _wide - 2, _y);
std::wcout << _top_right;
SetPos(_x, _y + _high - 1);
std::wcout << _bottom_left;
SetPos(_x + _wide - 2, _y + _high - 1);
std::wcout << _bottom_right;
}
void cleanCharUp()
{
SetPos(_x + 2, _y);
for (int wide = 0; wide <= _wide - 4; ++wide)
{
std::cout << ' ';
}
}
void cleanCharDown()
{
SetPos(_x + 2, _y + _high - 1);
for (int wide = 0; wide <= _wide - 4; ++wide)
{
std::cout << ' ';
}
}
void cleanCharLeft()
{
for (int high = 1; high <= _high - 2; ++high)
{
SetPos(_x, _y + high);
std::cout << ' ' << ' ';
}
}
void cleanCharRight()
{
for (int high = 1; high <= _high - 2; ++high)
{
SetPos(_x + _wide - 2, _y + high);
std::cout << ' ' << ' ';
}
}
void cleanCharCorner()
{
SetPos(_x, _y);
std::cout << ' ' << ' ';
SetPos(_x + _wide - 2, _y);
std::cout << ' ' << ' ';
SetPos(_x, _y + _high - 1);
std::cout << ' ' << ' ';
SetPos(_x + _wide - 2, _y + _high - 1);
std::cout << ' ' << ' ';
}
};
}
console_screen_frame.h
cpp
#pragma once
#include "console_screen_frame_char.h"
#include "console_screen_frame_wchar_t.h"
#include <vector>
#include <utility>
namespace my
{
enum FRAME_DIRECTION
{
Up = 0,
Down,
Left,
Right
};
template<class T, class CH, class STRING>
class console_screen_frame
{
template<class PFUNC, class MENU, class FRAME, class CHAR, class STRING>
friend class console_screen_menu;
template<class TEXTCHAR, class FRAME, class STRING, class CHAR>
friend class console_screen_text;
public:
console_screen_frame(int x, int y, int high, int wide,
CH up = 0, CH down = 0, CH left = 0, CH right = 0,
CH top_left = 0, CH bottom_left = 0, CH top_right = 0, CH bottom_right = 0)
:_the_char(x, y, high, wide, up, down, left, right
, top_left, bottom_left, top_right, bottom_right)
{
;
}
void setPos(int x, int y)
{
_the_char.changePos(x, y);
}
void setFrameSize(int high, int wide)
{
_the_char.changeFrameSize(high, wide);
}
void setSide(CH up = 0, CH down = 0, CH left = 0, CH right = 0)
{
_the_char.changeSide(up, down, left, right);
}
void setCorner(CH top_left = 0, CH bottom_left = 0,
CH top_right = 0, CH bottom_right = 0)
{
_the_char.changeCorner(top_left, bottom_left, top_right, bottom_right);
}
void setUp(CH up)
{
_the_char.changeUp(up);
}
void setDown(CH down)
{
_the_char.changeDown(down);
}
void setLeft(CH left)
{
_the_char.changeLeft(left);
}
void setRight(CH right)
{
_the_char.changeRight(right);
}
void setTopLeft(CH top_left)
{
_the_char.changeTopLeft(top_left);
}
void setTopRight(CH top_right)
{
_the_char.changeTopRight(top_right);
}
void setBottomLeft(CH bottom_left)
{
_the_char.changeBottomLeft(bottom_left);
}
void setBottomRight(CH bottom_right)
{
_the_char.changeBottomRight(bottom_right);
}
void frameShow()
{
_the_char.show();
if (sizeof(CH) == sizeof(signed char))
{
wordShow((void(*)(STRING))&getCout, (void(*)(CH))&getCoutC);
}
else
{
wordShow((void(*)(STRING))&getWcout, (void(*)(CH))&getWcoutC);
}
}
void frameClean()
{
_the_char.clean();
}
void numWord(int number)
{
_word.resize(number);
_word_space.resize(number);
}
void setWord(int pos, STRING str1, signed char direction, short distance)
{
assert(pos >= 0 && pos < _word.size());
_word[pos] = str1;
_word_space[pos] = { direction, distance };
}
void recallWord(int pos)
{
assert(pos >= 0 && pos < _word.size());
_word.erase(_word.begin() + pos);
_word_space.erase(_word_space.begin() + pos);
}
protected:
void wordShow(void(*outfuncString)(STRING), void(*outfuncChar)(CH))
{
for (int i = 0; i < _word.size(); ++i)
{
if (_the_char._up != 0 && _word_space[i].first == Up)
{
SetPos(_the_char._x + _word_space[i].second, _the_char._y);
if (_word[i] == nullptr)
{
continue;
}
outfuncString(_word[i]);
}
else if (_the_char._down != 0 && _word_space[i].first == Down)
{
SetPos(_the_char._x + _word_space[i].second, _the_char._y + _the_char._high - 1);
if (_word[i] == nullptr)
{
continue;
}
outfuncString(_word[i]);
}
else if (_the_char._left != 0 && _word_space[i].first == Left)
{
int len = clen(_word[i]);
for (int j = 0; j < len; ++j)
{
SetPos(_the_char._x, _the_char._y + _word_space[i].second + j);
if (_word[i] == nullptr)
{
continue;
}
outfuncChar(_word[i][j]);
}
}
else if (_the_char._right != 0 && _word_space[i].first == Right)
{
int len = clen(_word[i]);
for (int j = 0; j < len; ++j)
{
SetPos(_the_char._x + _the_char._wide - sizeof(CH), _the_char._y + _word_space[i].second + j);
if (_word[i] == nullptr)
{
continue;
}
outfuncChar(_word[i][j]);
}
}
}
}
static void getWcout(STRING str1)
{
std::wcout << str1;
}
static void getCout(STRING str1)
{
std::cout << str1;
}
static void getWcoutC(CH ch)
{
std::wcout << ch;
}
static void getCoutC(CH ch)
{
std::cout << ch;
}
protected:
std::vector<STRING> _word; // 嵌入词
std::vector<std::pair<signed char, short>> _word_space; // 嵌入词位置
T _the_char;
typedef size_t(*p_T_char)(STRING);
p_T_char clen = sizeof(CH) == sizeof(signed char) ? (p_T_char)&strlen : (p_T_char)&wcslen;
};
typedef console_screen_frame<console_screen_frame_char, signed char, const char*> frame;
typedef console_screen_frame<console_screen_frame_wchar_t, wchar_t, const wchar_t*> wframe;
}
menu
console_screen_menu_base.h
cpp
#pragma once
#include "console_screen_set.h"
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <Windows.h>
#include <utility>
namespace my
{
template<class T>
static T Max(T a, T b)
{
return a > b ? a : b;
}
typedef enum CONSOLE_SCREEN_COLOR
{
CBlack = 0,
CBlue,
CGreen,
CLight_blue,
CRed,
CPurple,
CYellow,
CWhite,
BBlack = 0,
BBlue = 0x10,
BGreen = 0x20,
BLight_blue = 0x30,
BRed = 0x40,
BPurple = 0x50,
BYellow = 0x60,
BWhite = 0x70
} the_color;
class console_screen_menu_base
{
public:
console_screen_menu_base(int x, int y, int rowNum = 1, int colNum = 1, int high = 1, int wide = 1)
:_x(x), _y(y), _high(high), _wide(wide), _rowNum(rowNum), _colNum(colNum)
, _option(rowNum, std::vector<std::string>(colNum, std::string("nullptr")))
, _char_space(rowNum, std::vector<std::pair<short, short>>(colNum, { 1, 1 }))
, _char_color(rowNum, std::vector<std::pair<short, bool>>(colNum, { CWhite, 0 }))
, _background_space(rowNum, std::vector<std::pair<short, short>>(colNum, { 0, 0 }))
, _background_color(rowNum, std::vector<std::pair<short, bool>>(colNum, { BBlack, 0 }))
{
assert(rowNum >= 1 && colNum >= 1);
assert(x >= 0 && y >= 0);
assert(high >= 0 && wide >= 0);
}
const std::string& getOption(int rowPos, int colPos)
{
assert(rowPos >= 1 && colPos >= 1);
return _option[rowPos - 1][colPos - 1];
}
void setOption(int rowPos, int colPos, const std::string& str1,
short front = 1, short back = 1)
{
assert(rowPos >= 1 && colPos >= 1);
assert(front >= 0 && back >= 0);
assert(rowPos <= _rowNum && colPos <= _colNum);
_option[rowPos - 1][colPos - 1] = str1;
_char_space[rowPos - 1][colPos - 1] = { front, back };
}
void setColor(int rowPos, int colPos, int charColor, bool charStrength, int backgroundColor = 0, bool backgroundStrength = false)
{
char_color(rowPos, colPos, charColor, charStrength);
background_color(rowPos, colPos, backgroundColor, backgroundStrength);
}
void setCharColor(int rowPos, int colPos, int colorNum, bool strength = false)
{
char_color(rowPos, colPos, colorNum, strength);
}
void setBackgroundColor(int rowPos, int colPos, int colorNum, bool strength = false)
{
background_color(rowPos, colPos, colorNum, strength);
}
void setCharSpace(int rowPos, int colPos, int front, int back)
{
assert(rowPos >= 1 && colPos >= 1);
_char_space[rowPos - 1][colPos - 1] = { front, back };
}
void setBackgroundSpace(int rowPos, int colPos, int front, int back)
{
assert(rowPos >= 1 && colPos >= 1);
_background_space[rowPos - 1][colPos - 1] = { front, back };
}
void setRowSpace(int row)
{
assert(row >= 0);
_row_space = row;
}
void setPos(int x, int y)
{
assert(x >= 0 && y >= 0);
_x = x;
_y = y;
}
void show()
{
show_or_clean_ready(true);
}
void clean()
{
show_or_clean_ready(false);
}
protected:
void char_color(int rowPos, int colPos, int colorNum, bool strength)
{
assert(CBlack <= colorNum && colorNum <= CWhite);
_char_color[rowPos - 1][colPos - 1] = { colorNum, strength };
}
void background_color(int rowPos, int colPos, int colorNum, bool strength)
{
assert(BBlack <= colorNum && colorNum <= BWhite);
_background_color[rowPos - 1][colPos - 1] = { colorNum, strength };
}
void clean_ready(int i, int j, int x, int y)
{
int len1 = _option[i][j].size();
if (_background_space[i][j].first > 0)
{
len1 += _background_space[i][j].first;
}
if (_background_space[i][j].second > 0)
{
len1 += _background_space[i][j].second;
}
// 补丁:正序打印会影响宽字体,逆向可解决
for (int i = len1 - 1; i >= 0; --i)
{
SetPos(x + i, y);
std::cout << ' ';
}
}
void show_ready(int i, int j)
{
int charStrength = 0;
int backgroundStrength = 0;
if (_background_color[i][j].second == true)
{
backgroundStrength = BACKGROUND_INTENSITY;
}
if (_char_color[i][j].second == true)
{
charStrength = FOREGROUND_INTENSITY;
}
if (_background_space[i][j].first > 0)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), _background_color[i][j].first | backgroundStrength);
std::cout << std::string(_background_space[i][j].first, ' ');
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), _char_color[i][j].first | charStrength | _background_color[i][j].first | backgroundStrength);
std::cout << _option[i][j];
if (_background_space[i][j].second > 0)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), _background_color[i][j].first | backgroundStrength);
std::cout << std::string(_background_space[i][j].second, ' ');
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), CWhite | BBlack);
}
void show_or_clean_ready(bool option)
{
int countPosY = 0;
for (int i = 0; i < _rowNum; ++i)
{
if (i != 0)
{
countPosY += _row_space;
}
int countPosX = 0;
for (int j = 0; j < _colNum; ++j)
{
int front = _char_space[i][j].first;
if (j != 0)
{
countPosX += _option[i][j - 1].size();
countPosX += _char_space[i][j - 1].first + _background_space[i][j - 1].first;
countPosX += _background_space[i][j - 1].second + _char_space[i][j - 1].second;
}
SetPos(_x + front + countPosX, _y + i + countPosY);
if (option == true)
{
show_ready(i, j);
}
else
{
clean_ready(i, j, _x + front + countPosX, _y + i + countPosY);
}
}
}
}
protected:
std::vector<std::vector<std::string>> _option; // 选项
//std::vector<std::vector<const char*>> _option;
std::vector<std::vector<std::pair<short, short>>> _char_space; // 字符间距
std::vector<std::vector<std::pair<short, bool>>> _char_color; // 字符 颜色 与 颜色加强
std::vector<std::vector<std::pair<short, short>>> _background_space; // 背景间距
std::vector<std::vector<std::pair<short, bool>>> _background_color; // 背景 颜色 与 颜色加强
short _x;
short _y;
short _high;
short _wide;
//short _rowNum = 1;
//short _colNum = 1;
int _rowNum = 1;
int _colNum = 1;
signed char _row_space = 0; // 行距
};
}
console_screen_menu_operate.h
cpp
#pragma once
#include "console_screen_set.h"
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <Windows.h>
#include <utility>
namespace my
{
template<class T>
static T Max(T a, T b)
{
return a > b ? a : b;
}
typedef enum CONSOLE_SCREEN_COLOR
{
CBlack = 0,
CBlue,
CGreen,
CLight_blue,
CRed,
CPurple,
CYellow,
CWhite,
BBlack = 0,
BBlue = 0x10,
BGreen = 0x20,
BLight_blue = 0x30,
BRed = 0x40,
BPurple = 0x50,
BYellow = 0x60,
BWhite = 0x70
} the_color;
class console_screen_menu_base
{
public:
console_screen_menu_base(int x, int y, int rowNum = 1, int colNum = 1, int high = 1, int wide = 1)
:_x(x), _y(y), _high(high), _wide(wide), _rowNum(rowNum), _colNum(colNum)
, _option(rowNum, std::vector<std::string>(colNum, std::string("nullptr")))
, _char_space(rowNum, std::vector<std::pair<short, short>>(colNum, { 1, 1 }))
, _char_color(rowNum, std::vector<std::pair<short, bool>>(colNum, { CWhite, 0 }))
, _background_space(rowNum, std::vector<std::pair<short, short>>(colNum, { 0, 0 }))
, _background_color(rowNum, std::vector<std::pair<short, bool>>(colNum, { BBlack, 0 }))
{
assert(rowNum >= 1 && colNum >= 1);
assert(x >= 0 && y >= 0);
assert(high >= 0 && wide >= 0);
}
const std::string& getOption(int rowPos, int colPos)
{
assert(rowPos >= 1 && colPos >= 1);
return _option[rowPos - 1][colPos - 1];
}
void setOption(int rowPos, int colPos, const std::string& str1,
short front = 1, short back = 1)
{
assert(rowPos >= 1 && colPos >= 1);
assert(front >= 0 && back >= 0);
assert(rowPos <= _rowNum && colPos <= _colNum);
_option[rowPos - 1][colPos - 1] = str1;
_char_space[rowPos - 1][colPos - 1] = { front, back };
}
void setColor(int rowPos, int colPos, int charColor, bool charStrength, int backgroundColor = 0, bool backgroundStrength = false)
{
char_color(rowPos, colPos, charColor, charStrength);
background_color(rowPos, colPos, backgroundColor, backgroundStrength);
}
void setCharColor(int rowPos, int colPos, int colorNum, bool strength = false)
{
char_color(rowPos, colPos, colorNum, strength);
}
void setBackgroundColor(int rowPos, int colPos, int colorNum, bool strength = false)
{
background_color(rowPos, colPos, colorNum, strength);
}
void setCharSpace(int rowPos, int colPos, int front, int back)
{
assert(rowPos >= 1 && colPos >= 1);
_char_space[rowPos - 1][colPos - 1] = { front, back };
}
void setBackgroundSpace(int rowPos, int colPos, int front, int back)
{
assert(rowPos >= 1 && colPos >= 1);
_background_space[rowPos - 1][colPos - 1] = { front, back };
}
void setRowSpace(int row)
{
assert(row >= 0);
_row_space = row;
}
void setPos(int x, int y)
{
assert(x >= 0 && y >= 0);
_x = x;
_y = y;
}
void show()
{
show_or_clean_ready(true);
}
void clean()
{
show_or_clean_ready(false);
}
protected:
void char_color(int rowPos, int colPos, int colorNum, bool strength)
{
assert(CBlack <= colorNum && colorNum <= CWhite);
_char_color[rowPos - 1][colPos - 1] = { colorNum, strength };
}
void background_color(int rowPos, int colPos, int colorNum, bool strength)
{
assert(BBlack <= colorNum && colorNum <= BWhite);
_background_color[rowPos - 1][colPos - 1] = { colorNum, strength };
}
void clean_ready(int i, int j, int x, int y)
{
int len1 = _option[i][j].size();
if (_background_space[i][j].first > 0)
{
len1 += _background_space[i][j].first;
}
if (_background_space[i][j].second > 0)
{
len1 += _background_space[i][j].second;
}
// 补丁:正序打印会影响宽字体,逆向可解决
for (int i = len1 - 1; i >= 0; --i)
{
SetPos(x + i, y);
std::cout << ' ';
}
}
void show_ready(int i, int j)
{
int charStrength = 0;
int backgroundStrength = 0;
if (_background_color[i][j].second == true)
{
backgroundStrength = BACKGROUND_INTENSITY;
}
if (_char_color[i][j].second == true)
{
charStrength = FOREGROUND_INTENSITY;
}
if (_background_space[i][j].first > 0)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), _background_color[i][j].first | backgroundStrength);
std::cout << std::string(_background_space[i][j].first, ' ');
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), _char_color[i][j].first | charStrength | _background_color[i][j].first | backgroundStrength);
std::cout << _option[i][j];
if (_background_space[i][j].second > 0)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), _background_color[i][j].first | backgroundStrength);
std::cout << std::string(_background_space[i][j].second, ' ');
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), CWhite | BBlack);
}
void show_or_clean_ready(bool option)
{
int countPosY = 0;
for (int i = 0; i < _rowNum; ++i)
{
if (i != 0)
{
countPosY += _row_space;
}
int countPosX = 0;
for (int j = 0; j < _colNum; ++j)
{
int front = _char_space[i][j].first;
if (j != 0)
{
countPosX += _option[i][j - 1].size();
countPosX += _char_space[i][j - 1].first + _background_space[i][j - 1].first;
countPosX += _background_space[i][j - 1].second + _char_space[i][j - 1].second;
}
SetPos(_x + front + countPosX, _y + i + countPosY);
if (option == true)
{
show_ready(i, j);
}
else
{
clean_ready(i, j, _x + front + countPosX, _y + i + countPosY);
}
}
}
}
protected:
std::vector<std::vector<std::string>> _option; // 选项
//std::vector<std::vector<const char*>> _option;
std::vector<std::vector<std::pair<short, short>>> _char_space; // 字符间距
std::vector<std::vector<std::pair<short, bool>>> _char_color; // 字符 颜色 与 颜色加强
std::vector<std::vector<std::pair<short, short>>> _background_space; // 背景间距
std::vector<std::vector<std::pair<short, bool>>> _background_color; // 背景 颜色 与 颜色加强
short _x;
short _y;
short _high;
short _wide;
//short _rowNum = 1;
//short _colNum = 1;
int _rowNum = 1;
int _colNum = 1;
signed char _row_space = 0; // 行距
};
}
console_screen_menu.h
cpp
#pragma once
#include "console_screen_frame.h"
#include "console_screen_menu_operate.h"
namespace my
{
typedef enum console_screen_menu_operate_way
{
keyboard = 1,
mouse,
} way;
template<class PFUNC = int(*)()
,class MENU = console_screen_menu_operate<PFUNC>
, class FRAME = frame
, class CHAR = signed char
, class STRING = const char*>
class console_screen_menu
{
public:
console_screen_menu(int x = 0, int y = 0, int high = 10, int wide = 20, int rowNum = 1, int colNum = 1, bool operate_switch = false)
:_frame(x, y, high, wide)
,_menu(x, y, operate_switch, rowNum, colNum, high, wide)
{
;
}
public: // 自定义操作准备
template<class T1, class T2, class T3, class T4>
int operateCustom(int (*customize)(T1&, T2&, T3&, T4&), T2& fun1, T3& fun2, T4& fun3)
{
return customize(*this, fun1, fun2, fun3);
}
template<class T1, class T2, class T3>
int operateCustom(int (*customize)(T1&, T2&, T3&), T2& fun1, T3& fun2)
{
return customize(*this, fun1, fun2);
}
template<class T1, class T2>
int operateCustom(int (*customize)(T1&, T2&), T2& fun1)
{
return customize(*this, fun1);
}
template<class T>
int operateCustom(int (*customize)(T&))
{
return customize(*this);
}
void customSetMenuCharColor(int rowPos, int colPos, int colorNum, bool strength = false)
{
_menu.setCharColor(rowPos, colPos, colorNum, strength);
}
const std::string& customGetMenuOption(int rowPos, int colPos)
{
return _menu.getOption(rowPos, colPos);
}
void customSetMenuOption(int rowPos, int colPos, const std::string& str1, short front = 0, short back = 0)
{
setMenuOption(rowPos, colPos, str1, front, back);
}
void customMouseAndOptionAc(int posX, int posY)
{
_menu.mouseAndOptionAc(posX, posY);
}
THE_POS customGetMouseCheckAlgorithm(int mouseX, int mouseY)
{
return _menu.mouseAndOptionDockingAlgorithm(mouseX, mouseY);
}
void customGetMouseOperate()
{
_menu.getMouseOperate();
}
void customOperateClean()
{
_menu.operateClean();
}
int customGetSleepTime()
{
return _menu._sleep_time;
}
PFUNC customGetOptionButton(int cursorX, int cursorY)
{
assert(cursorX >= 0 && cursorX < _menu._colNum);
assert(cursorY >= 0 && cursorY < _menu._rowNum);
return _menu._the_way[cursorY][cursorX];
}
int customGetCursorColNum()
{
return _menu._colNum;
}
int customGetCursorX()
{
return _menu._cursorPosX;
}
void customSetCursorX(int cursorX)
{
_menu.cursorXReset(cursorX);
}
int customGetCursorRowNum()
{
return _menu._rowNum;
}
int customGetCursorY()
{
return _menu._cursorPosY;
}
//short customGetOptionRowNum()
//{
// return _menu._rowNum;
//}
//short customGetOptionY()
//{
// return _menu._cursorPosY;
//}
void customSetCursorY(int cursorY)
{
_menu.cursorYReset(cursorY);
}
void customSetCursorPos(int cursorX, int cursorY)
{
_menu.cursorReset(cursorX, cursorY);
}
void customOperateShow()
{
_menu.operateShow();
}
bool customMenuCheckOperate()
{
return _menu._open_operate;
}
public: // 静态准备
void staticInit(bool yes = true)
{
_staticInit = yes;
}
bool alreadyStaticInit()
{
return _staticInit;
}
public: // 有关菜单与框架
void setPosCompare(short x, short y)
{
assert(x >= 0);
assert(y >= 0);
_compare_x = x;
_compare_y = y;
_menu.setPos(_menu._x + x, _menu._y + y);
}
void setAndCheckPos(int x, int y)
{
_frame.setPos(x, y);
_menu.setPos(x, y);
checkPosFrameWithMenu();
}
void show()
{
_menu.show();
_frame.frameShow();
}
void clean()
{
_menu.clean();
_frame.frameClean();
}
public: // 只关于菜单的
void setMaxSpaceOper()
{
_menu.setMaxSpaceOper();
}
void setSameSpaceOperWithBack()
{
_menu.setSameSpaceOperWithBack();
}
void setMenuButton(int rowPos, int colPos, PFUNC customize)
{
_menu.setWay(rowPos, colPos, customize);
}
void setOperateSpace(int rowPos, int colPos, int front, int back)
{
_menu.setOperateSapce(rowPos, colPos, front, back);
}
void setOperateColor(int rowPos, int colPos, int colorNum, bool strength = false)
{
_menu.setOperateColor(rowPos, colPos, colorNum, strength);
}
void operateMode(bool Yes = false)
{
_menu.operateMode(Yes);
}
int operateWay(int way, int optionX = 1, int optionY = 1)
{
int info = 0;
if (way == keyboard)
{
info = _menu.operateKeyboardStart(optionX, optionY);
}
else if (way == mouse)
{
info = _menu.operateMouseStart();
}
return info;
}
//void operateMouseStart()
//{
// _menu.operateMouseStart();
//}
//void operateKeyboardStart(int posX = 0, int posY = 0)
//{
// _menu.operateKeyboardStart(posX, posY);
//}
void setMenuRowSpace(int row)
{
_menu.setRowSpace(row);
}
void setMenuBackgroundSpace(int rowPos, int colPos, int front, int back)
{
_menu.setBackgroundSpace(rowPos, colPos, front, back);
}
void setMenuCharSpace(int rowPos, int colPos, int front, int back)
{
_menu.setCharSpace(rowPos, colPos, front, back);
}
void setMenuBackgroundColor(int rowPos, int colPos, int colorNum, int strength = false)
{
_menu.setBackgroundColor(rowPos, colPos, colorNum, strength);
}
void setMenuCharColor(int rowPos, int colPos, int colorNum, int strength = false)
{
_menu.setCharColor(rowPos, colPos, colorNum, strength);
}
void setMenuColor(int rowPos, int colPos, int charColor, bool charStrength, int backgroundColor = 0, bool backgroundStrength = false)
{
_menu.setColor(rowPos, colPos, charColor, charStrength, backgroundColor, backgroundStrength);
}
void setMenuOption(int rowPos, int colPos, const std::string& str1, short front = 1, short back = 1)
{
_menu.setOption(rowPos, colPos, str1, front, back);
}
public: // 只关于框架的
void setFrameSide(CHAR up, CHAR down, CHAR left , CHAR right)
{
_frame.setSide(up, down, left, right);
checkPosFrameWithMenu();
}
void setFrameCorner(CHAR top_left, CHAR bottom_left, CHAR top_right, CHAR bottom_right)
{
_frame.setCorner(top_left, bottom_left, top_right, bottom_right);
checkPosFrameWithMenu();
}
void setFrameNumWord(int number)
{
_frame.numWord(number);
}
void setFrameWord(int pos, STRING str1, signed char direction, short distance)
{
_frame.setWord(pos, str1, direction, distance);
}
void setFrameRecallWord(int pos)
{
_frame.recallWord(pos);
}
protected:
void checkPosFrameWithMenu()
{
if (_frame._the_char._x != _menu._x && _frame._the_char._y != _menu._y)
{
return;
}
short up = 0;
short down = 0;
short left = 0;
short right = 0;
if (_frame._the_char._up != 0)
{
up = 1;
}
if (_frame._the_char._left != 0)
{
left = sizeof(CHAR);
}
if (_frame._the_char._down != 0)
{
down = 1;
}
if (_frame._the_char._right != 0)
{
right = 1;
}
_menu.setPos(_frame._the_char._x + left + _compare_x, _frame._the_char._y + up + _compare_y);
}
protected:
FRAME _frame;
MENU _menu;
short _compare_x = 0;
short _compare_y = 0;
bool _staticInit = false;
};
typedef console_screen_menu<int(*)(), console_screen_menu_operate<int(*)()>, frame, signed char, const char*> menu;
typedef console_screen_menu<int(*)(), console_screen_menu_operate<int(*)()>, wframe, wchar_t, const wchar_t*> wmenu;
}
text
console_screen_text_base.h
cpp
#pragma once
#include <vector>
#include <utility>
#include <cassert>
#include <iostream>
namespace my
{
template<class STRING, class CH>
class console_screen_text_base
{
template<class TEXTCHAR, class FRAME, class STRING, class CHAR>
friend class console_screen_text;
public:
console_screen_text_base(short x, short y, short high = 10, short wide = 10, int textNum = 1)
:_text_num(textNum, std::pair<STRING, short>(nullptr, 0))
,_x(x)
,_y(y)
,_high(high)
,_wide(wide)
{
assert(textNum > 0);
assert(high > 0 && wide > 0);
assert(x >= 0 && y >= 0);
}
void setText(int theTextRank, STRING theText, short frontDistance)
{
assert(theTextRank > 0);
assert(theText != nullptr && frontDistance >= 0);
_text_num[theTextRank - 1] = { theText, frontDistance };
}
void setRowSpace(int row)
{
assert(row >= 0);
_row_space = row;
}
void setHideAndWide(int high, int wide)
{
assert(high > 0 && wide > 0);
_high = high;
_wide = wide;
}
void showNoCheck()
{
if (sizeof(CH) == sizeof(signed char))
{
textShowClean((void(*)(STRING))&getCout, true);
}
else
{
textShowClean((void(*)(STRING)) & getWcout, true);
}
}
void cleanNoCheck()
{
if (sizeof(CH) == sizeof(signed char))
{
textShowClean((void(*)(STRING)) & getCout, false);
}
else
{
textShowClean((void(*)(STRING)) & getWcout, false);
}
}
void cleanAndCheck()
{
if (sizeof(CH) == sizeof(signed char))
{
textShowCleanAndCheck((void(*)(CH)) & getCoutC, false);
}
else
{
textShowCleanAndCheck((void(*)(CH)) & getWcoutC, false);
}
}
void showAndCheck()
{
if (sizeof(CH) == sizeof(signed char))
{
textShowCleanAndCheck((void(*)(CH)) & getCoutC, true);
}
else
{
textShowCleanAndCheck((void(*)(CH)) & getWcoutC, true);
}
}
void setPos(short x, short y)
{
assert(x >= 0 && y >= 0);
_x = x;
_y = y;
}
protected:
//文本长度受宽度限制,过长会换行
void textShowCleanAndCheck(void(*outfuncString)(CH), bool isShow)
{
int countPosY = 0;
int wideLine = _wide;
if (sizeof(CH) == 2)
{
wideLine /= 2;
}
for (int i = 0; i < _text_num.size(); ++i)
{
if (i != 0)
{
countPosY += _row_space;
}
if (_text_num[i].first == nullptr)
{
return;
}
int len = theStrlen(_text_num[i].first);
int j = 0;
int printIndex = 0;
if (sizeof(CH) == 1)
{
printIndex = _text_num[i].second;
}
else
{
printIndex = _text_num[i].second % 2;
printIndex = _text_num[i].second / 2;
}
SetPos(_x + _text_num[i].second, _y + i + countPosY);
while (j < wideLine - printIndex)
{
if (isShow == true)
{
outfuncString(_text_num[i].first[j]);
}
else
{
CH blank = 0;
if (sizeof(CH) == 2)
{
blank = L' ';
outfuncString(blank);
outfuncString(blank);
}
else
{
blank = ' ';
outfuncString(blank);
}
}
++j;
}
++countPosY;
while (printIndex < len)
{
SetPos(_x, _y + i + countPosY);
printIndex = j;
int maxSize = wideLine + printIndex;
if (maxSize > len)
{
maxSize = len;
}
while (j < maxSize)
{
if (isShow == true)
{
outfuncString(_text_num[i].first[j]);
}
else
{
CH blank = 0;
if (sizeof(CH) == 2)
{
blank = L' ';
outfuncString(blank);
outfuncString(blank);
}
else
{
blank = ' ';
outfuncString(blank);
}
}
++j;
}
if (j < len)
{
++countPosY;
}
}
}
}
void textShowClean(void(*outfuncString)(STRING), bool isShow)
{
int countPosY = 0;
for (int i = 0; i < _text_num.size(); ++i)
{
if (i != 0)
{
countPosY += _row_space;
}
SetPos(_x + _text_num[i].second, _y + i + countPosY);
if (_text_num[i].first == nullptr)
{
return;
}
if (isShow == true)
{
outfuncString(_text_num[i].first);
}
else
{
int len = theStrlen(_text_num[i].first);
if (sizeof(CH) == 1)
{
std::cout << std::string(len, ' ');
}
else
{
std::wcout << std::wstring(len * 2, L' ');
}
}
}
}
static void getWcout(STRING str1)
{
std::wcout << str1;
}
static void getCout(STRING str1)
{
std::cout << str1;
}
static void getWcoutC(CH ch)
{
std::wcout << ch;
}
static void getCoutC(CH ch)
{
std::cout << ch;
}
protected:
std::vector<std::pair<STRING, short>> _text_num; // 文本存储
short _row_space = 0; // 行距
short _x;
short _y;
short _high;
short _wide;
typedef size_t(*p_T_char)(STRING);
p_T_char theStrlen = sizeof(CH) == sizeof(signed char) ? (p_T_char)&strlen : (p_T_char)&wcslen;
};
typedef console_screen_text_base<const char*, signed char> text_base;
typedef console_screen_text_base<const wchar_t*, wchar_t> wtext_base;
}
console_screen_text.h
cpp
#pragma once
#include "console_screen_frame.h"
#include "console_screen_text_base.h"
namespace my
{
template<class TEXTCHAR, class FRAME, class STRING, class CHAR>
class console_screen_text
{
public:
console_screen_text(short x, short y, short high = 10, short wide = 20, int textNum = 1)
:_text(x, y, high, wide, textNum)
, _frame(x, y, high, wide)
{
;
}
public: // 静态准备
void staticInit(bool yes = true)
{
_staticInit = yes;
}
bool alreadyStaticInit()
{
return _staticInit;
}
public: // 有关文本与框架
void setPosCompare(short x, short y)
{
assert(x >= 0);
assert(y >= 0);
_compare_x = x;
_compare_y = y;
setTextPos(_text._x + x, _text._y + y);
}
void checkPosFrameWithText()
{
if (_frame._the_char._x != _text._x && _frame._the_char._y != _text._y)
{
return;
}
short up = 0;
short down = 0;
short left = 0;
short right = 0;
if (_frame._the_char._up != 0)
{
up = 1;
}
if (_frame._the_char._left != 0)
{
left = sizeof(CHAR);
}
if (_frame._the_char._down != 0)
{
down = 1;
}
if (_frame._the_char._right != 0)
{
right = 1;
}
setTextPos(_frame._the_char._x + left + _compare_x, _frame._the_char._y + up + _compare_y);
_text._high = _frame._the_char._high - up * 2;
_text._wide = _frame._the_char._wide - left * 2;
}
void setAndCheckPos(int x, int y)
{
_frame.setPos(x, y);
_text.setPos(x, y);
checkPosFrameWithText();
}
void show()
{
_frame.frameShow();
_text.showNoCheck();
}
void showAndCheck()
{
_frame.frameShow();
_text.showAndCheck();
}
void clean()
{
_frame.frameClean();
_text.cleanNoCheck();
}
void cleanAndCheck()
{
_frame.frameClean();
_text.cleanAndCheck();
}
public: // 只关于文本的
void setTextPos(short x, short y)
{
_text.setPos(x, y);
}
//void showAndCheck()
//{
// _text.showAndCheck();
//}
//void showButNoCheck()
//{
// _text.showButNoCheck();
//}
void setHideAndWide(int high, int wide)
{
_text.setRowSpace(high, wide);
}
void setRowSpace(int row)
{
_text.setRowSpace(row);
}
void setText(int theTextRank, STRING text, short frontDistance)
{
_text.setText(theTextRank, text, frontDistance);
}
public: // 只关于框架的
void setFrameSide(CHAR up, CHAR down, CHAR left, CHAR right)
{
_frame.setSide(up, down, left, right);
checkPosFrameWithText();
}
void setFrameCorner(CHAR top_left, CHAR bottom_left, CHAR top_right, CHAR bottom_right)
{
_frame.setCorner(top_left, bottom_left, top_right, bottom_right);
checkPosFrameWithText();
}
void setFrameNumWord(int number)
{
_frame.numWord(number);
}
void setFrameWord(int pos, STRING str1, signed char direction, short distance)
{
_frame.setWord(pos, str1, direction, distance);
}
void setFrameRecallWord(int pos)
{
_frame.recallWord(pos);
}
protected:
TEXTCHAR _text;
FRAME _frame;
short _compare_x = 0;
short _compare_y = 0;
bool _staticInit = false;
};
typedef console_screen_text<text_base, frame, const char*, signed char> text;
typedef console_screen_text<wtext_base, wframe, const wchar_t*, wchar_t> wtext;
}
四、代码参考
后续可能会出一份使用菜单库的博客,这里只给一个菜单供大家参考。
cpp
#include "console_screen_menu.h"
#include <iostream>
int isQuit()
{
return 1;
}
int main()
{
std::wcout.imbue(std::locale("zh_CN"));
my::HideCursor();
static my::wmenu isATest(2, 1, 5, 10, 2, 1, true);
if (isATest.alreadyStaticInit() == false)
{
isATest.setMenuOption(1, 1, "开始", 0, 0);
isATest.setMenuOption(2, 1, "结束", 0, 0);
isATest.setFrameSide(L'□', L'■', L'○', L'●');
isATest.setFrameCorner(L'△', L'▲', L'▽', L'▼');
isATest.setMenuBackgroundSpace(1, 1, 0, 2);
isATest.setMenuBackgroundSpace(2, 1, 0, 2);
isATest.setOperateSpace(1, 1, 2, 0);
isATest.setOperateSpace(2, 1, 2, 0);
isATest.setMenuButton(2, 1, isQuit);
isATest.staticInit(true);
}
int operWay = 1;
isATest.show();
if (operWay == 1)
{
isATest.operateWay(my::keyboard);
}
else
{
isATest.operateWay(my::mouse);
}
return 0;
}