起因:程序设计大作业(计算器、校园卡管理系统、象棋游戏)越来越复杂,设计界面和键盘输入报错过于繁琐。我查阅网络资料,发现windows.h属于我认识的范畴,于是开始查阅、学习。以下是部分小结,面向初学者。
句柄(handle):
控制台(console):
光标(cursor):
windows.h中,
有鼠标坐标的结构体COORD,并增加输出坐标、判定点击等功能
有鼠标事件的结构体MOUSE_EVENT_RECORD,含位置、按键、事件类型等信息
通过SetConsoleTextAttribute函数,设置控制台输出的颜色
console.hpp
#ifndef CONSOLE_HPP
#define CONSOLE_HPP
#include
#include
#include
#include
using std::ios_base;
using std::ostream;
//dwButtonState 鼠标按钮状态
#define L_BUTTON 0x1
#define R_BUTTON 0x2
// dwEventFlags 鼠标事件状态
#define MOUSE_CLICK 0x0
#define MOUSE_MOVED 0x1
#define DOUBLE_CLICK 0x2
//颜色
#define COLOR_default SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);
#define COLOR_Black_Cyan SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0b);
#define COLOR_Yellow_Blue SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xe9);
// 移动光标位置
void setcursor(short x = 0, short y = 0)
{
COORD temp = {x, y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), temp);
}
void setcursor(const COORD &temp)
{
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), temp);
}
// 获得光标位置
void getcursor(COORD &other)
{
CONSOLE_SCREEN_BUFFER_INFO temp;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &temp);
other = temp.dwCursorPosition;
}
COORD getcursor()
{
CONSOLE_SCREEN_BUFFER_INFO temp;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &temp);
return temp.dwCursorPosition;
}
//输出光标坐标
ostream &operator<<(ostream &pout, const COORD &temp)
{
pout.setf(ios_base::fixed);
pout << "[Cursor Position] X: " << std::setw(2) << temp.X << " Y: " << std::setw(2) << temp.Y;
return pout;
}
//是否隐藏光标
void hidecursor(bool hide = true)
{
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);
CursorInfo.bVisible = !hide;
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &CursorInfo);
}
//button - click
//判断鼠标点击位置是否在指定的有效范围(本行,本列至5列之间)
bool operator-(const COORD &button, const COORD &click)
{
if (button.Y == click.Y && button.X <= click.X && click.X <= button.X + 20)
return true;
else
return false;
};
// 更改控制台设置,防止控制台属性导致的判定失败
void setmode()
{
DWORD mode;
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode);
mode &= ~ENABLE_QUICK_EDIT_MODE; //移除快速编辑模式
mode &= ~ENABLE_INSERT_MODE; //移除插入模式
// mode &= ~ENABLE_MOUSE_INPUT; //移除鼠标输入
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), mode);
}
//等待鼠标事件,可排除鼠标移动
MOUSE_EVENT_RECORD waitmouse(bool move = true)
{
INPUT_RECORD record; //输入事件
DWORD reg; //临时寄存
while (1)
{
Sleep(10);
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &record, 1, ®); //将输入事件存入record
if (record.EventType == MOUSE_EVENT && (move | record.Event.MouseEvent.dwEventFlags != MOUSE_MOVED)) //是鼠标事件 && 移动事件与模式对应
return record.Event.MouseEvent;
}
}
// 清屏函数
void clean()
{
COORD temp = getcursor();
setcursor(0, 0);
for (int i = 0; i <= temp.Y; i++)
std::cout << " \n";
setcursor(0, 0);
}
//暂停
void pause()
{
std::cout << "请按任意键继续\n";
std::cin.sync();
std::cin.get();
}
#endif
输入事件:(我会把与本次操作无关的部分注释掉)
struct INPUT_RECORD
{
WORD EventType; //事件类型
union
{
struct MOUSE_EVENT_RECORD
{
COORD dwMousePosition; // 当前鼠标位置
DWORD dwButtonState; // 鼠标按钮状态
DWORD dwControlKeyState; // 键盘控制键状态
DWORD dwEventFlags; // 鼠标事件状态
} MouseEvent;
// KEY_EVENT_RECORD KeyEvent;
// WINDOW_BUFFER_SIZE_RECORD WindowBufferSizeEvent;
// MENU_EVENT_RECORD MenuEvent;
// FOCUS_EVENT_RECORD FocusEvent;
} Event;
};
部分有关常量 :
// 事件类型
#define KEY_EVENT 0x1 //键盘事件
#define MOUSE_EVENT 0x2 //鼠标事件
// 鼠标按钮状态
#define FROM_LEFT_1ST_BUTTON_PRESSED 0x1 //鼠标左键
#define RIGHTMOST_BUTTON_PRESSED 0x2 //鼠标右键
// 鼠标事件状态
#define MOUSE_MOVED 0x1 //移动
#define DOUBLE_CLICK 0x2 //双击
有关颜色信息:
点击型菜单,点击相应选项(输出的字符)进入函数功能
#ifndef MENU_HPP
#define MENU_HPP
#include "console.hpp"
#include
using std::cout;
using std::endl;
using std::string;
using std::vector;
class Menu
{
private:
struct node
{
COORD pos_;
string display_;
void (*pf_)();
node(string d = "Unkown.", void (*p)() = nullptr) : display_(d), pf_(p){};
};
vector nodes_;
string title_;
protected:
void recordposition()
{
for (auto p = nodes_.begin(); p != nodes_.end(); p++)
{
getcursor(p->pos_);
cout << p->display_ << "\n\n";
}
};
bool implement(COORD clickpos)
{
for (auto p = nodes_.begin(); p != nodes_.end(); p++)
{
if (p->pos_ - clickpos) //点击成功
{
hidecursor(0);
clean();
p->pf_();
hidecursor(1);
return true;
}
}
return false;
};
void highlight(COORD hang)
{
clean();
cout << title_ << endl //显示内容
<< "========================================" << endl;
for (auto p = nodes_.begin(); p != nodes_.end(); p++)
{
if (p->pos_ - hang) //悬停点
{
COLOR_Black_Cyan;
cout << p->display_ << "\n\n";
COLOR_default;
}
else
cout << p->display_ << "\n\n";
}
}
public:
//constructor
Menu(){};
~Menu(){};
//methods
Menu &settitle(string s)
{
title_ = s;
return *this;
}
Menu &add(void (*p)() = nullptr, string d = "Unkown.")
{
nodes_.push_back(node(d, p));
return *this;
};
void start()
{
setmode();
MOUSE_EVENT_RECORD mouse;
hidecursor(1);
clean();
cout << title_ << endl //显示内容
<< "========================================" << endl;
recordposition(); //记录位置
do
{
Sleep(10); //减少次数
mouse = waitmouse();
if (mouse.dwEventFlags == MOUSE_MOVED)
highlight(mouse.dwMousePosition);
else if (mouse.dwButtonState == L_BUTTON)
implement(mouse.dwMousePosition);
} while (mouse.dwButtonState != R_BUTTON);
Sleep(100); //过滤
};
};
#endif
void show1() { cout << "choice1\n"; }
void show2() { cout << "choice2\n"; }
int main(void)
{
Menu loop;
void (*p)();
p = show1;
loop.settitle("test").add(p, "show1").add(show2, "show2");
loop.start();
}
参考信息:
https://blog.csdn.net/bnb45/article/details/8042819
https://blog.csdn.net/o_longzhong/article/details/80276645