提问人:zMxrlxn 提问时间:11/16/2023 最后编辑:πάντα ῥεῖzMxrlxn 更新时间:11/16/2023 访问量:54
C++ 命令行更新文本居中
C++ Command Line updating Text Centering
问:
项目
所以我试图编写一个具有不同命令行场景的工具,如“菜单”、“功能”、“设置”等。
我也希望这个工具是动画的,我发现,我可以通过创建一个 Map 来做到这一点,该 Map 将 int 作为键(用于命令行中的位置 (y*width)+x) 和 wchar_t/wstring 作为值(显示在该位置的字符)。
地图应该每 16 毫秒 (~60 FPS) 显示一次,这样我就可以制作“更流畅”的动画。
我的问题是我希望我的徽标显示在屏幕中央,在我尝试调整窗口大小之前,效果很好。
我试图在循环中检查命令行窗口的大小并尝试调整它的大小,但它只是工作不佳......
看这里(质量真的很差,不知道为什么)
法典
main.cpp
#include <iostream>
#include <fcntl.h>
#include <thread>
#include <chrono>
#include <windows.h>
#include "scene/Scene.cpp"
#include "scene/scenes/MenuScene.cpp"
#include "scene/scenes/FeatureScene.cpp"
#include "scene/scenes/SettingsScene.cpp"
using namespace std;
MenuScene menuScene;
FeatureScene featureScene;
SettingsScene settingsScene;
Scene currentScene = menuScene;
void displayCharMap(map<int, wchar_t> charMap) {
int whitespaces = 0;
for(int i = 0; i < currentScene.getWidth()*currentScene.getHeight(); i++) {
if(i % currentScene.getWidth() == 0) {
charMap[i-1] = L'\n';
}
if(charMap[i] == L' ') {
whitespaces++;
} else {
wcout << repeat(L" ", whitespaces) << charMap[i];
whitespaces = 0;
}
}
}
int main() {
// Init
cout << "\033[2J"; // Clear displayed Lines
cout << "\033[3J"; // Clear previous Lines
cout << "\033[H"; // Goto Home (position: 0, 0)
cout << "\033[?25l"; // Make Cursor Invisible
cout << flush;
CONSOLE_SCREEN_BUFFER_INFO csbi;
_setmode(_fileno(stdout), _O_U16TEXT); // Set Output Encoding to UTF-16
int bufferWidth;
int bufferHeight;
// On Tick
while(true) {
// Resize CharMap
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
bufferWidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
bufferHeight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
if(currentScene.getWidth() != bufferWidth || currentScene.getHeight() != bufferHeight) {
currentScene.setWidth(bufferWidth);
currentScene.setHeight(bufferHeight);
currentScene.resetCharMap();
currentScene.insertMultiLineString(bufferWidth, center(logo, bufferWidth));
}
// Display CharMap
wcout << "\033[H"; // Goto Home (position: 0, 0)
map<int, wchar_t> charMap = currentScene.getCharMap();
displayCharMap(charMap);
wcout << flush;
// Sleep
this_thread::sleep_for(chrono::milliseconds(16));
}
}
场景.cpp
#include <sstream>
#include "Scene.h"
map<int, wchar_t> Scene::getCharMap() {
return charMap;
}
void Scene::insert(int position, wstring str) {
for(int i = 0; i < str.length(); i++) {
charMap[position+i] = str[i];
}
}
void Scene::resetCharMap() {
for(int i = 0; i < width*height; i++) {
if(charMap[i] == L' ') {
charMap[i] = L' ';
}
}
}
void Scene::insertMultiLineString(int position, const wstring& str) {
int currentPosition = position;
wistringstream wiss(str);
wstring line;
while(getline(wiss, line)) {
insert(currentPosition, line + L'\n');
currentPosition += width;
}
}
场景.h
#include <map>
#include <string>
#include "../util/stringutil.cpp"
#ifndef AIOTOOL_SCENE_H
#define AIOTOOL_SCENE_H
using namespace std;
const wstring logo = L"┌──────────────────────────────────────────────────────────────┐\n"
L"│ │\n"
L"│ ░█████╗░██╗░█████╗░ ████████╗░█████╗░░█████╗░██╗░░░░░ │\n"
L"│ ██╔══██╗██║██╔══██╗ ╚══██╔══╝██╔══██╗██╔══██╗██║░░░░░ │\n"
L"│ ███████║██║██║░░██║ ░░░██║░░░██║░░██║██║░░██║██║░░░░░ │\n"
L"│ ██╔══██║██║██║░░██║ ░░░██║░░░██║░░██║██║░░██║██║░░░░░ │\n"
L"│ ██║░░██║██║╚█████╔╝ ░░░██║░░░╚█████╔╝╚█████╔╝███████╗ │\n"
L"│ ╚═╝░░╚═╝╚═╝░╚════╝░ ░░░╚═╝░░░░╚════╝░░╚════╝░╚══════╝ │\n"
L"│ │\n"
L"└──────────────────────────────────────────────────────────────┘";
class Scene {
private:
map<int, wchar_t> charMap;
int width;
int height;
public:
Scene() {
this->width = 120;
this->height = 30;
insertMultiLineString(width, center(logo, width));
}
Scene(int _width, int _height) {
this->width = _width;
this->height = _height;
insertMultiLineString(width, center(logo, width));
}
map<int, wchar_t> getCharMap();
void insert(int position, wstring str);
void resetCharMap();
void insertMultiLineString(int position, const wstring& str);
[[nodiscard]] int getWidth() const {
return width;
}
[[nodiscard]] int getHeight() const {
return height;
}
void setWidth(int _width) {
this->width = _width;
}
void setHeight(int _height) {
this->height = _height;
}
};
#endif //AIOTOOL_SCENE_H
字符串util.cpp
#include <string>
#include <cmath>
#include <codecvt>
#include <locale>
#include <sstream>
#pragma once
using namespace std;
string repeat(string str, int times) {
string repeated;
for(int i = 0; i < times; i++) {
repeated += str;
}
return repeated;
}
wstring repeat(wstring str, int times) {
wstring repeated;
for (int i = 0; i < times; i++) {
repeated += str;
}
return repeated;
}
string center(string str, int width) {
wstring_convert<codecvt_utf8<char32_t>, char32_t> conv;
string output;
istringstream iss(str);
string line;
while(getline(iss, line)) {
u32string u32line = conv.from_bytes(line);
int diff = width - u32line.length();
output.append(repeat(" ", static_cast<int>(round(diff/2.0f))) + line + repeat(" ", static_cast<int>(floor(diff/2.0f))) + "\n");
}
return output.substr(0, output.length()-1);
}
wstring center(wstring str, int width) {
wstring output;
wistringstream wiss(str);
wstring line;
while(getline(wiss, line)) {
int diff = width - line.length();
output.append(wstring(round(((float)diff)/2.0f), L' ') + line + wstring(floor(((float)diff)/2.0f), L' ') + L"\n");
}
return output.substr(0, output.length()-1);
}
我试过了什么
我在 Google 上搜索了如何正确地居中多行字符串,但没有太多的上下文,或者以我对 c++ 编程的基本知识来说,它们根本不容易实现。
答: 暂无答案
评论