自制按键显示的程序
文章目录
- 自制按键显示的程序
- 0. 介绍
- 1. 界面设计
- 2. 代码
- 3. 效果
- 4. 总结
- References
0. 介绍
在看一些 up 主讲解 vim/emacs 等软件的界面操作时,使用了显示按键的软件。
据说蒋炎炎讲课用的是自己写的,一度以为很神奇。 现在我们用 SFML 手搓一个极其简陋的 demo 版本。
1. 界面设计
不需要复杂界面, 不需要托管图标, 不需要隐藏窗口, 不需要动态变化的窗口。
就弄一个长条形的窗口即可。 不考虑多行显示。
清除内容: 每当用户超过1秒没有输入内容, 或者本次输入内容和上次输入相差了1秒以上, 就清空以前的内容。
如果连续两次按下的按键,在1秒之内, 那么追加内容。 判断1秒, 是通过判断60帧来决定的, 因为我设定了 fps=60.
2. 代码
严格说,用 SFML 的 text 显示输入文本,不是特别合适, 比如可能只需要一次渲染而不是每个字符渲染一次。 但是没关系, 原型设计,20分钟写出代码验证想法。
关于键盘按键的表示:看一下官方 tutorial 文档,以及 SFML 源代码中 include/SFML/Window/keyboard.hpp . 简单起见我只处理了A-Z的字符, 以及Ctrl, Esc, [ 和 ].
#include <iostream>
#include <vector>
#include <iterator>
#include <SFML/Graphics.hpp>int main()
{constexpr int win_width = 800;constexpr int win_height = 200;const std::string title = "show typed keys - SFML";sf::RenderWindow window(sf::VideoMode(win_width, win_height), title);window.setFramerateLimit(60);sf::Font font;const std::string asset_dir = "../../games/Resources";if (!font.loadFromFile(asset_dir + "/Arial.ttf")){std::cerr << "failed to load font\n";return 1;}std::vector<std::string> keys;int frame_index = 0;int prev_press_frame = 0;while (window.isOpen()){int press_frame = -1;sf::Event event;std::vector<std::string> cur_keys;while (window.pollEvent(event)){switch (event.type){case sf::Event::Closed: window.close(); break;case sf::Event::KeyReleased: // see include/SFML/Window/Keyboard.hpp for refif (event.key.code >= sf::Keyboard::Key::A && event.key.code <= sf::Keyboard::Key::Z){std::string key(1, 'A' + (event.key.code - sf::Keyboard::Key::A));cur_keys.push_back(key);}if (event.key.code == sf::Keyboard::Key::Space) cur_keys.push_back(" ");if (event.key.code == sf::Keyboard::Key::LControl) cur_keys.push_back("Ctrl");if (event.key.code == sf::Keyboard::Key::RControl) cur_keys.push_back("Ctrl");if (event.key.code == sf::Keyboard::Key::Escape) cur_keys.push_back("Esc");if (event.key.code == sf::Keyboard::Key::LBracket) cur_keys.push_back("[");if (event.key.code == sf::Keyboard::Key::RBracket) cur_keys.push_back("]");press_frame = frame_index;break;default:break;}}window.clear();printf("[debug] frame_index=%d, press_frame=%d, prev_press_frame=%d\n",frame_index, press_frame, prev_press_frame);if (cur_keys.size() > 0){if (press_frame - prev_press_frame >= 60){keys.clear();keys = cur_keys;}else{std::copy(cur_keys.begin(), cur_keys.end(), std::back_inserter(keys));}prev_press_frame = press_frame;}else if (cur_keys.size() == 0){if (frame_index - prev_press_frame >= 60){keys.clear();frame_index = 0;prev_press_frame = 0;}}std::string content = "";for (int i = 0; i < keys.size(); i++){content = content + keys[i];}sf::Text text;text.setFont(font);text.setString(content);text.setCharacterSize(30);text.setFillColor(sf::Color::White);text.setPosition(20, 20);window.draw(text);window.display();frame_index++;}return 0;
}
3. 效果
4. 总结
由于先前几篇的练习, 对 SFML 有了基本的使用经验的情况下, 对于显示按键的实现能够快速上手。 主要思考点在于,怎样显示按键内容, 怎样清空内容。 通过判断相邻两次按键的时间差,大于1秒就清空, 否则保持原内容。 通过使用 switch case 语句,简化了 A-Z 的26个字母的判断。
这是一个非常简陋的 demo, 有时间的话可以做改进:
- 界面改进: 显示多行, 把空格显示为
|-|
的字符 - 框架库的更换: 使用 GUI 库,例如 imgui 作为替代
- 添加托盘:需要为每个操作系统分别适配
References
- https://www.sfml-dev.org/tutorials/2.6/window-inputs.php
- https://cloud.tencent.com/developer/article/2167806