用Easyx图形库实现类似windows下拉菜单系统

编译环境:Vs2019 + Easyx图形库

const int MenuBarHeight		 = 24;		// 菜单栏高度
const int MenuBarWidth		 = 60;		// 菜单栏宽度

struct MenuItem {
	std::string text;           // 菜单文本
	std::vector<MenuItem> subItems; // 二级菜单项
	RECT rect;    // 菜单项位置和尺寸
	bool isSubMenu;             // 是否为二级菜单
};
class DropdownMenu {
private:
	std::vector<MenuItem> mainMenu; // 主菜单项
	int activeMainMenu;          // 当前激活的主菜单索引
	int activeSubMenu;           // 当前激活的二级菜单索引
	bool showSubMenu;            // 是否显示二级菜单
	bool m_enable;               // 菜单是否能操作
	COLORREF backgroundColor;
	COLORREF textColor;
	COLORREF mainhoverbkColor;
	COLORREF mainhovertextColor;
	COLORREF subhoverbkColor;
	COLORREF subhovertextColor;
	COLORREF borderColor;

	// 检查鼠标是否在菜单区域内(包括主菜单和二级菜单)
	bool isMouseInMenuArea(int mouseX, int mouseY) {
		// 检查主菜单
		for (size_t i = 0; i < mainMenu.size(); ++i) {
			if (InSide(mouseX, mouseY, mainMenu[i].rect)) {
				return true;
			}
		}
		// 检查当前显示的二级菜单
		if (showSubMenu && activeMainMenu >= 0) {
			RECT r = mainMenu[activeMainMenu].subItems[0].rect;
			r.bottom = r.bottom + ((int)mainMenu[activeMainMenu].subItems.size() - 1) * MenuBarHeight;
			if (InSide(mouseX, mouseY, r)) {
				return true;
			}
		}
		return false;
	}


public:
	DropdownMenu() :activeMainMenu(-1), activeSubMenu(-1), showSubMenu(false), m_enable(true) {
		// 初始化颜色
		backgroundColor = RGB(222, 227, 243);
		textColor = BLUE;
		mainhoverbkColor = RGB(75,0,96);
		mainhovertextColor = RGB(82,255,255);
		subhoverbkColor = RGB(159,0,90);
		subhovertextColor = WHITE;
		borderColor = RGB(213, 194, 176);

		// 主菜单项
		mainMenu = {
			{"游戏(G)", {{"开局          F2"}, {"撤销           "}, {"提示          F1"}, {"退出        ESC"}}, {0, 0, MenuBarWidth, MenuBarHeight}, true},
			{"设置(S)", {{"播放音乐        "},{"显示动画         "}, {"显示辅助        "}},{MenuBarWidth,0,2 * MenuBarWidth,MenuBarHeight},true},
			{"帮助(H)", {{"游戏说明        "}, {"关于FreeCell     "}}, {2 * MenuBarWidth, 0, 3 * MenuBarWidth, MenuBarHeight}, true},
		};
		for (int i = 0; i < (int)mainMenu.size(); i++) {
			for (int j = 0; j < (int)mainMenu[i].subItems.size(); j++) {
				mainMenu[i].subItems[j].rect = { mainMenu[i].rect.left + 10,mainMenu[i].rect.bottom + j * MenuBarHeight - 1, //偏移一点以免从一级菜单移动到二级菜单时检测不到
												mainMenu[i].rect.left + 120,mainMenu[i].rect.bottom + (j + 1) * MenuBarHeight - 1 };
			}
		}
	}
	void Draw()
	{
		settextstyle(20, 0, "微软雅黑");
		setbkmode(TRANSPARENT);
		// 绘制主菜单
		for (size_t i = 0; i < mainMenu.size(); i++) {
			setlinecolor(borderColor);
			// 绘制背景
			if (activeMainMenu == i) { 
				setfillcolor(mainhoverbkColor); 
				settextcolor(mainhovertextColor);
			}
			else {
				setfillcolor(backgroundColor);
				settextcolor(textColor);
			}
			fillrectangle(mainMenu[i].rect.left, mainMenu[i].rect.top, mainMenu[i].rect.right, mainMenu[i].rect.bottom);
			// 绘制文字
			drawtext((LPCSTR)mainMenu[i].text.c_str(), &mainMenu[i].rect, DT_VCENTER | DT_SINGLELINE | DT_CENTER);
		}

		// 绘制二级菜单
		if (showSubMenu && activeMainMenu >= 0) {
			for (size_t i = 0; i < mainMenu[activeMainMenu].subItems.size(); i++) {
				if (activeSubMenu == i) { 
					setfillcolor(subhoverbkColor); 
					settextcolor(subhovertextColor); 
				}
				else {
					setfillcolor(backgroundColor);
					settextcolor(textColor);
				}
				fillrectangle(mainMenu[activeMainMenu].subItems[i].rect.left, mainMenu[activeMainMenu].subItems[i].rect.top, mainMenu[activeMainMenu].subItems[i].rect.right, mainMenu[activeMainMenu].subItems[i].rect.bottom);
				drawtext((LPCSTR)("  " + mainMenu[activeMainMenu].subItems[i].text).c_str(), &mainMenu[activeMainMenu].subItems[i].rect, DT_VCENTER | DT_SINGLELINE);
			}
		}
	}

	// 处理事件
	void handleEvent(ExMessage& msg) {
		if (!m_enable) return;
		if (msg.message == WM_LBUTTONDOWN && activeMainMenu >= 0 && activeSubMenu >= 0)
		{
			msg.x = 0; msg.y = 0;
			int i = activeMainMenu;
			int j = activeSubMenu;
			activeMainMenu = -1;
			activeSubMenu = -1;
			showSubMenu = false;
			FlushBatchDraw();
			// 运行回调函数,可能需要i,j,上面的代码放下面画面会延迟更新
			callback(i, j);
		}
		if (msg.message == WM_MOUSEMOVE) {

			// 检查鼠标是否在菜单区域内
			bool mouseInMenu = isMouseInMenuArea(msg.x, msg.y);
			if (!mouseInMenu)
			{
				activeMainMenu = -1;
				activeSubMenu = -1;
				showSubMenu = false;
				return;
			}
			for (int i = 0; i < (int)mainMenu.size(); i++) {
				if (InSide(msg.x, msg.y, mainMenu[i].rect))
				{
					activeMainMenu = i;
					showSubMenu = true;
				}
			}
			if (showSubMenu && activeMainMenu >= 0) {
				for (int i = 0; i < (int)mainMenu[activeMainMenu].subItems.size(); i++) {
					if (InSide(msg.x, msg.y, mainMenu[activeMainMenu].subItems[i].rect)) {
						activeSubMenu = i;
					}
				}
			}
		}
		if (msg.message == WM_KEYDOWN) {
			if (msg.vkcode == VK_ESCAPE) callback(0, 3);
			if (msg.vkcode == VK_F1) callback(0, 2);
			if (msg.vkcode == VK_F2) callback(0, 0);
		}
	}
	// 修改子菜单的标题文字
	void ChangeSubMenuText(int MainID, int SubID, const string& text)
	{
		mainMenu[MainID].subItems[SubID].text = text;
		Draw();
	}
	// 设置菜单启用状态
	void SetEanble(bool isEnable) {
		m_enable = isEnable;
		backgroundColor = m_enable ? RGB(222, 227, 243) : RGB(222, 222, 222);
		textColor = m_enable ? RGB(0, 0, 0) : RGB(213, 194, 176);
	}
	void(*callback)(int MainID, int SubID);
};
posted @ 2026-01-03 15:36  让泪化作相思雨  阅读(8)  评论(0)    收藏  举报