Wie kann ich Editline für eine In-Game-Konsole verwenden?

9

Ich möchte einem C ++ - Spiel, das ich erstelle , eine In-Game- Konsole hinzufügen . Obwohl das Rendern der Konsole und das Parsen der Befehle, mit denen ich einverstanden bin, scheint der Aspekt der Texteingabe und -bearbeitung (z. B. Umgang mit linken / rechten Tasten, Leerzeichen usw.) eine Menge Aufwand zu sein, den ich lieber für interessantere Dinge aufwenden würde .

Ich habe es geschafft, eine Bibliothek namens " Editline" zu finden , die die Kernpunkte der Texteingabe im Konsolenstil mit Verlauf und automatischer Vervollständigung behandelt. Ich bin mir jedoch nicht sicher, wie ich dies mit meinem Spiel verbinden soll - insbesondere mit allen Beispielen Ich habe festgestellt, dass ich die Standard-Eingabe- und Ausgabe-Handles verwende und readline()selbst ein blockierender Aufruf ist. Ich muss in der Lage sein, meine eigene Eingabe bereitzustellen und die Konsole für meine eigene Ausgabe zu rendern, und ich muss auch sicherstellen, dass meine Spieleschleife weiter läuft.

Wie soll ich das machen?

Justin
quelle

Antworten:

4

Die Editline-Bibliothek (und die GNU-Readline) funktionieren nur mit Terminalfunktionen. Um diese Bibliotheken mit Ihrer In-Game-Konsole zu verwenden, müssen Sie zunächst einen Terminal-Emulator (TTY) implementieren .

Stattdessen (da dies für ein Spiel verrückt wäre) würde ich Ihnen empfehlen, Cursorbewegungen zu implementieren und sich selbst zu bearbeiten. Ich habe einmal eine C ++ - Klasse geschrieben, um dies für meine eigene In-Game-Konsole genau zu diesem Zweck zu handhaben. Die Klasse heißt treffend EditLine. Die Klasse verarbeitet grundlegende Cursorbewegungen, Vorwärts- / Rückwärtszeichen- / Wort- / Zeilenlöschungen und den Verlauf. Nichts Besonderes, aber vielleicht finden Sie es nützlich. Die Dokumentation ist spärlich oder nicht vorhanden, hoffentlich können Sie es trotzdem herausfinden. Leider keine automatische Vervollständigung. Ich habe das in einer oberen Ebene mit dieser Klasse implementiert.

editline.h

#include <string>
#include <vector>

class EditLine {
public:
    EditLine();
    EditLine(const EditLine&);

    ~EditLine();

    // assignment operator
    EditLine& operator=(const EditLine&);

    // comparison operators
    bool operator==(const EditLine&) const;
    bool operator!=(const EditLine&) const;

    // edit commands
    void accept_line();
    void reject_line();
    void insert_char(int);
    void delete_char();
    void backward_delete_char();
    void delete_word();
    void backward_delete_word();
    void delete_line();

    // motion commands
    void beginning_of_line();
    void end_of_line();
    void forward_char();
    void backward_char();
    void forward_word();
    void backward_word();

    // history commands
    void next_history();
    void previous_history();

    // accessors
    int history_pos() const;
    inline size_t history_size() const;
    inline bool empty() const;
    inline const std::string& line() const;
    inline size_t length() const;
    inline const std::string::size_type& cursor_pos() const;

    // mutators
    void set_line(const std::string& s);
    void set_cursor_pos(std::string::size_type);
    void reset_line();

private:
    std::string line_;
    std::string::size_type cursor_pos_;
    std::vector< std::string > history_;
    std::vector< std::string >::iterator last_iter_;
};

inline size_t EditLine::history_size() const { return history_.size(); }
inline const std::string& EditLine::line() const { return line_; }
inline const std::string::size_type& EditLine::cursor_pos() const { return cursor_pos_; }
inline bool EditLine::empty() const { return line_.empty(); }
inline size_t EditLine::length() const { return line_.length(); }

editline.cpp

#include "editline.h"
#include "string_utils.h"
#include <string>

namespace {
    const std::string word_delims = " !@#$%^&*()+-={}[]:\"|;'\\<>?,./";
} // namespace

EditLine::EditLine()
    : line_(std::string()),
      cursor_pos_(0),
      last_iter_(history_.end())
{

}

EditLine::EditLine(const EditLine& rhs)
{
    *this = rhs;
}

EditLine::~EditLine()
{

}

EditLine& EditLine::operator=(const EditLine& rhs)
{
    line_ = rhs.line_;
    cursor_pos_ = rhs.cursor_pos_;
    history_ = rhs.history_;
    if (rhs.last_iter_ == rhs.history_.end())
        last_iter_ = history_.end();
    else {
        last_iter_ = history_.begin();
        std::advance(last_iter_, rhs.last_iter_ - rhs.history_.begin());
    }
    return *this;
}

void EditLine::set_line(const std::string& s)
{
    line_ = s;
    cursor_pos_ = line_.size();
}

void EditLine::set_cursor_pos(std::string::size_type pos)
{
    if (pos > line_.size())
        pos = line_.size();
    cursor_pos_ = pos;
}

void EditLine::reset_line()
{
    line_.clear();
    cursor_pos_ = 0;
}

bool EditLine::operator==(const EditLine& rhs) const
{
    return (line_         == rhs.line_       &&
            cursor_pos_   == rhs.cursor_pos_ &&
            history_      == rhs.history_    &&
            history_pos() == rhs.history_pos());
}

bool EditLine::operator!=(const EditLine& rhs) const
{
    return !operator==(rhs);
}

void EditLine::accept_line()
{
    if (!line_.empty())
        history_.push_back(line_);

    line_.clear();
    cursor_pos_ = 0;
    last_iter_ = history_.end();
}

void EditLine::reject_line()
{
    line_.clear();
    cursor_pos_ = 0;
    last_iter_ = history_.end();
}

void EditLine::insert_char(int c)
{
    line_.insert(cursor_pos_, 1, c);
    cursor_pos_++;
}

void EditLine::delete_char()
{
    line_.erase(cursor_pos_, 1);
}

void EditLine::backward_delete_char()
{
    if (cursor_pos_ > 0) {
        line_.erase(cursor_pos_ - 1, 1);
        cursor_pos_--;
    }
}

void EditLine::delete_word()
{
    std::string::size_type pos;

    // check if cursor points on a word delim
    if (word_delims.find(line_[cursor_pos_]) != std::string::npos) {
        // cursor points on a word delim - remove everything from here to
        // right up to first delim after this word.
        pos = line_.find_first_not_of(word_delims, cursor_pos_+1);
        if (pos != std::string::npos)
            pos = line_.find_first_of(word_delims, pos+1);
    } else {
        // cursor is in the middle of a word - remove everything up to first
        // delim.
        pos = line_.find_first_of(word_delims, cursor_pos_+1);
    }

    if (pos != std::string::npos)
        // removes everything right of cursor up to 'pos'
        line_.replace(cursor_pos_, pos - cursor_pos_, "");
    else
        // removes everthing right of cursor
        line_.erase(cursor_pos_);
}

void EditLine::backward_delete_word()
{
    std::string::size_type pos;

    if (cursor_pos_ == 0) return;

    // check if char left of cursor is a word delim
    if (word_delims.find(line_[cursor_pos_-1]) != std::string::npos) {
        // left of cursor is a word delim - remove everything from left of
        // cursor up to next word delim before current word.
        pos = rfind_first_not_of(line_, word_delims, cursor_pos_-1);
        if (pos != std::string::npos)
            pos = rfind_first_of(line_, word_delims, pos);
    } else {
        // left of cursor is not a word delim - remove everything left of
        // cursor up to next word delim.
        pos = rfind_first_of(line_, word_delims, cursor_pos_-1);
    }

    if (pos != std::string::npos) {
        // removes from right of pos and up to cursor
        line_.erase(pos + 1, cursor_pos_ - pos - 1);
        cursor_pos_ = pos + 1;
    } else {
        // removes everything from beginning up to cursor
        line_.erase(0, cursor_pos_);
        cursor_pos_ = 0;
    }
}

void EditLine::delete_line()
{
    line_.erase(cursor_pos_);
    cursor_pos_ = line_.size();
}

void EditLine::beginning_of_line()
{
    cursor_pos_ = 0;
}

void EditLine::end_of_line()
{
    cursor_pos_ = line_.size();
}

void EditLine::forward_char()
{
    if (cursor_pos_ < line_.size())
        cursor_pos_++;
}

void EditLine::backward_char()
{
    if (cursor_pos_ > 0)
        cursor_pos_--;
}

void EditLine::forward_word()
{
    std::string::size_type pos;

    pos = line_.find_first_of(word_delims, cursor_pos_);

    if (pos != std::string::npos)
        cursor_pos_ = pos + 1;
    else
        cursor_pos_ = line_.size();
}

void EditLine::backward_word()
{
    if (cursor_pos_ <= 1) {
        cursor_pos_ = 0;
        return;
    }

    std::string::size_type pos, cursor_pos;

    cursor_pos = cursor_pos_;

    if (cursor_pos >= 2)
        cursor_pos -= 2;

    bool found = false;

    for (int i = (int) cursor_pos; i >= 0; --i) {
        if (word_delims.find(line_[i]) != std::string::npos) {
            pos = (std::string::size_type) i;
            found = true;
            break;
        }
    }

    if (found)
        cursor_pos_ = pos + 1;
    else
        cursor_pos_ = 0;
}

void EditLine::next_history()
{
    if (!history_.empty()) {
        if (last_iter_ == history_.end() || ++last_iter_ == history_.end())
            last_iter_ = history_.begin();
        line_ = *last_iter_;
        cursor_pos_ = line_.size();
    }
}

void EditLine::previous_history()
{
    if (!history_.empty()) {
        if (last_iter_ != history_.begin())
            --last_iter_;
        else
            last_iter_ = --history_.end();

        line_ = *last_iter_;
        cursor_pos_ = line_.size();
    }
}

int EditLine::history_pos() const
{
    if (last_iter_ == history_.end())
        return -1;
    return last_iter_ - history_.begin();
}

string_utils.h

std::string::size_type rfind_first_not_of(const std::string& s,
                                          const std::string& delims,
                                          std::string::size_type pos);

std::string::size_type rfind_first_of(const std::string& s,
                                      const std::string& delims,
                                      std::string::size_type pos);

string_utils.cpp

std::string::size_type rfind_first_not_of(const std::string& s,
        const std::string& delims, std::string::size_type pos)
{
    std::string::size_type p = pos;
    while (delims.find(s[p]) != std::string::npos) {
        if (p == 0) // not found
            return std::string::npos;
        --p;
    }
    return p;
}

std::string::size_type rfind_first_of(const std::string& s,
        const std::string& delims, std::string::size_type pos)
{
    std::string::size_type p = pos;
    while (delims.find(s[p]) == std::string::npos) {
        if (p == 0) // not found
            return std::string::npos;
        --p;
    }
    return p;
}
Oskar N.
quelle
0

Es sieht nicht nach einem allzu großen Problem aus. Das erste, was Sie wissen müssen, ist, dass Standard In und Standard Out nur Streams sind - sie können bei Bedarf durch Ihre eigenen ersetzt werden. Es gab eine Frage zu SO , und AFAIK, die einzigen zwei Möglichkeiten sind, entweder in den Code von Editline zu gehen und ihn zu ändern, um Ihre Streams zu verwenden, oder zu verwenden cin.rdbuf()und cout.rdbuf()- diese werden von der Standardbibliothek bereitgestellt und die Dokumentation finden Sie hier . Persönlich würde ich die Verwendung von Letzterem empfehlen - es ist nicht hackisch und beinhaltet nur das Hinzufügen von 2 Codezeilen zu Ihrem Spiel.

Polar
quelle
Nur dass libedit auch von ncurses abhängt und eine vollständige Terminal (TTY)
Oskar N.