当我使用键盘中的箭头键移动到该文本时,如何在 C++ 中为文本着色

问题描述 投票:0回答:1

我正在编写一个关于从给定日期查找一周中的某一天的小项目。我创建了一个菜单,有 3 个选项(按数字选择): 1. 功能 1、2. 功能 2、0. 退出程序。 菜单将如下所示:

#include <iomanip>
#include <iostream>
#include <string.h>
#include <Windows.h>
#include <conio.h> //dung de sdung getch(), text color
#include <bits/stdc++.h>
#include <vector>
#include <fstream> //doc file


using namespace std;

vector<pair<int, string> > days{{0,"SATURDAY"}, {1, "SUNDAY"}, {2,"MONDAY"}, {3,"TUESDAY"},
                            {4,"WEDNESDAY"}, {5,"THURSDAY"}, {6,"FRIDAY"}};


bool validDate(int d, int m, int y, char c) //Check input 
{
    if(c == '/')
    {
    if(y >= 1 && y <= 2999)
    {
        if(m >= 1 && m <= 12)
        {
           if(d >= 1 && d <= 31)
            {
                if( (d >= 1 && d <= 30) && (m == 4|| m == 6|| m == 9|| m == 11))
                    return true;
                else if((d >= 1 && d <= 30) && (m == 1 || m == 3 || m == 5 || m == 7 || m == 8|| m == 10 ||m == 12))
                    return true;
                else if((d >= 1 && d <= 28) && (m == 2))
                    return true;
                else if(d == 29 && m == 2 && ((y % 400 == 0)||(y % 4 == 0 && y % 100 != 0))) //leapyear
                    return true;
                else
                    return false;
            }
            else return false;
        }
        else return false;
    }
    else return false;
    }
    else return false;
}


/*
The following code is Zeller congruence method to calculate the day of given date
*/
int ZellerCongruence(int givenday, int givenmonth, int givenyear)
{
    if(givenmonth == 1) //In Zeller congruence, Jan & Feb is 13th and 14th month in the previous year
    {
        givenmonth = 13;
        givenyear--;
    }
    if(givenmonth == 2)
    {
        givenmonth = 14;
        givenyear--;
    }
    int q = givenday;
    int m = givenmonth;
    int k = givenyear % 100;
    int j = givenyear / 100;
    int h = q + (13 * (m + 1) / 5) + k + (k / 4) + (j / 4) + (5 * j);
    h = h % 7;
    return h;
}


void SubMenu1()
{
    cout << endl << "FIND THE DAY OF THE WEEK FROM THE GIVEN DATE" << endl;
    cout << endl <<  "Notice: Program can only calculate the year from 1 to 2999" << endl;
    cout << endl << "Enter date in dd/mm/yyyy format and press enter: ";
    int givenday, givenmonth, givenyear;
    char c;
    cin >> givenday;
    cin >> c;
    cin >> givenmonth;
    cin >> c;
    cin >> givenyear;
    if(validDate(givenday, givenmonth, givenyear, c) == false)
    {
        cout << "Invalid Date \n";
        Sleep(200);
        return;
    }
    int h = ZellerCongruence(givenday, givenmonth, givenyear);
    cout << endl << "The given day of date is: " << days[h].second << endl;
}


void SubMenu2()
{
    cout << "FIND THE DAY OF THE WEEK AFTER N DAYS FROM THE GIVEN DATE" << endl;
    cout << endl <<  "Notice: Program can only calculate the year from 1 to 2999" << endl;
    cout << endl << "Enter date in dd/mm/yyyy format and press enter: ";
    int givenday, givenmonth, givenyear;
    char c;
    cin >> givenday;
    cin >> c;
    cin >> givenmonth;
    cin >> c;
    cin >> givenyear;
    if(validDate(givenday, givenmonth, givenyear, c) == false)
    {
        cout << "Invalid Date \n";
        Sleep(200);
        return;
    }
    int h = ZellerCongruence(givenday, givenmonth, givenyear);
    int n;
    cout << "Enter the value of n: ";
    cin >> n;
    if(n <= 0)
    {
        cout << "Invalid value, n must be a positive interger." << endl;
        Sleep(200);
        return;
    }
    h = h + n;
    if(h < 7)
    {
        cout << "The day of the week after " << n << " days from the given date is: " << days[h].second << endl;
    }
    else
    {
        h = h % 7;
        cout << "The day of the week after " << n << " days from the given date is: " << days[h].second << endl;
    }
}


void Menu()
{
    cout << endl << "MAIN MENU" << endl;
    cout << "1. Find the day of the week from the given date" << endl;
    cout << "2. Find the day of the week after n days from the given date" << endl;
    cout << "0. Quit the program" << endl;
}

int main()
{
    int choice;

    start:
    system("CLS");
    Menu();
    cout<<"Enter Your Choice: ";

    s:
    char c = getch(); // getch() method pauses the Output Console until a key is pressed
    if(c >= '0' && c <= '2')
    {
        choice = c - 48; // translate the char values to the int values (cause '0' == 48 in ASCII). We can also write: choice = c - '0'
        cout << c;
        goto s2;
    }
    else
    {
        choice = getch();
    }
    goto s;

    s2:
    Sleep(200);
    system("CLS");
    switch(choice)
        {
            case 0:
                cout << "See you again !" << endl;
                return 0;
                break;
            case 1:
                SubMenu1();
                system("pause");
                system("CLS");
                goto start;
                break;
            case 2:
                SubMenu2();
                system("pause");
                system("CLS");
                goto start;
                break;
            default:
                cout << "Invalid Number..."<<endl;
                system("pause");
                system("CLS");
                goto start;
                break;
        }
        return 0;
}

我想问是否可以改进选择方式,从按数字改为使用箭头键。此外,我希望每次箭头键移动时,该处的文本行都会改变颜色。你能帮我吗?

c++ user-interface textcolor
1个回答
0
投票

正如评论已经说过的,更改终端输出颜色取决于平台,并且可能因您使用的每个终端而异。由于您使用 Windows,我认为我的解决方案也应该适用于您的计算机。

设置终端文本颜色

您可以通过多种方式更改窗口中的终端文本颜色,例如

system("color 0A")
是最常见的方式之一。但是,不建议这样做,并且您只有一小部分可能的颜色(背景和前景各 16 种)。

这就是为什么我更喜欢使用转义序列(请参阅这篇文章)将文本颜色更改为某个 RGB 值,如以下函数所示:

void setColor(std::uint8_t r, std::uint8_t g, std::uint8_t b) {
    // https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences

    // print an escape sequence which lets us control the text color in the windows terminal with RGB values
    std::string output{ "\x1b[38;2;" };
    output += std::to_string(r) + ";";
    output += std::to_string(g) + ";";
    output += std::to_string(b) + "m";

    std::cout << output;
}

获取方向键

要注册是否按下了某些箭头键,您可以使用例如来自 Windows API 的

GetKeyState()
,但我更喜欢使用一些普通的文本输入函数。由于您已经使用了非标准
<conio.h>
标头,我也这样做了,因为我认为以这种方式实现它非常容易。从 here 我获得了
arrowKeyIndicator
的值以及按下箭头键时从
_getch()
getch()
已弃用)返回的键的值。

我的函数等待用户输入(正在阻塞)并返回用户按下的箭头键。当用户按下 ENTER 键(或者准确地说是

'\r'
)时,我的函数按设计返回无效键,但您可以更改它以满足您的需要。

enum Key : unsigned char {
    None = 0, ArrowUp = 72, ArrowDown = 80, ArrowLeft = 75, ArrowRight = 77
};

Key getArrowKey() {
    // https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797

    constexpr unsigned char arrowKeyIndicator{ 224 };

    unsigned char ch{};
    // loop until the user presses either an arrow key (which will get us two chars)
    // or the user presses enter
    while ((ch = static_cast<char>(_getch())) != arrowKeyIndicator) {
        // return when the user presses enter (so there is a way to exit the loop without having to click an arrow key)
        if (ch == '\r') {
            return Key::None;
        }
    }

    // the first char was some character indicating arrow keys which is the same for all of them
    // now we get the second char
    ch = static_cast<char>(_getch());
    // and we return the corresponding arrow key
    for (const Key k : { Key::ArrowUp, Key::ArrowDown, Key::ArrowLeft, Key::ArrowRight }) {
        if (ch == k) {
            return k;
        }
    }

    // if it was not a arrow key, return `Key::None` (or could be another value to mark failure to get an arrow key)
    return Key::None;
}

使用示例

如果您想测试这两个功能,您可以使用以下代码片段来完成。请注意 print 语句末尾的

'\r'
(您当然可以将其移动到字符串文字中),它允许您写入同一行,而不是
'\n'
它将转到下一行。因此,您可以更改颜色并再次打印文本。但请注意,重写比以前更短的输出将使旧行中的一些字符仍然可见,因此您要么需要使用不同的方法,要么只是确保在使用
'\r'
时不要打印更小的行。

int main() {
    Key k{};

    do {
        std::cout << "When you press an arrow key, the color changes. If you press ENTER, the program terminates." << '\r';
        k = getArrowKey();

        switch (k)
        {
        case Key::ArrowUp:
            setColor(0xFF, 0x00, 0x00);
            break;
        case Key::ArrowDown:
            setColor(0x00, 0xFF, 0x00);
            break;
        case Key::ArrowLeft:
            setColor(0x00, 0x00, 0xFF);
            break;
        case Key::ArrowRight:
            setColor(0xFF, 0xA5, 0x00);
            break;
        default:
            break;
        }

    } while (k != Key::None);

    return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.