C++ 命令行更新文本居中

C++ Command Line updating Text Centering

提问人:zMxrlxn 提问时间:11/16/2023 最后编辑:πάντα ῥεῖzMxrlxn 更新时间:11/16/2023 访问量:54

问:

项目

所以我试图编写一个具有不同命令行场景的工具,如“菜单”、“功能”、“设置”等。
我也希望这个工具动画的,我发现,我可以通过创建一个 Map 来做到这一点,该 Mapint 作为键(用于命令行中的位置 (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++ 编程的基本知识来说,它们根本不容易实现。

C++ 控制台应用程序

评论

1赞 chrysante 11/16/2023
你能为你的问题创建一个最小的可重现的例子吗?
1赞 wohlstad 11/16/2023
我建议你从一本合适的书中开始学习 C++。请参阅此处的列表:权威 C++ 书籍指南和列表

答: 暂无答案