如何在我的 C++ 应用程序中重用 Qt 的 QTextCursor?

How do I reuse Qt's QTextCursor in my C++ application?

我正在 Windows 上使用 C++ 中的 Qt 实现一个文本编辑器,并使用 QWidget 作为我使用 OpenGL 显示文本的表面。到目前为止,我已经有了自己的布局引擎和文档模型,并且能够让文本显示在小部件上。

现在我正在尝试实现一个文本游标,其使用方式类似于 Qt 提供的游标,但 QTextCursor 与其 QTextDocument 模型紧密相关,我无法将其子类化以重新使用我的模型。有什么方法可以在没有模型的情况下仅重用光标吗?

如果没有,我该如何使用 Qt 实现文本光标?

注意:我确实使用了 Windows 提供的插入符方法 here,但我希望避免直接使用它们。

TL;DR:你不能。这不是插入符号。

QTextCursor 是一个迭代器,它与屏幕上的光标无关。如果它作为迭代器很有用,并且如果你自己的文本表示是建立在 QTextDocument 之上,你当然可以重用它。但它不是插入符号。

可见光标控件由 QTextEdit 实现提供。回想一下,QTextEditQTextDocument 的视图 - 它完全用于文本的图形渲染、控制可见光标等。

没有可用于插入符号的 public Qt API。当您使用 Qt 进行渲染时,WINAPI 插入符方法完全没有用。您需要自己的插入符实现。假设您已经有一个文本表示和一个渲染器,您大概有一个在文本表示上工作的迭代器,因此实现插入符号应该是一件微不足道的事情。

WINAPI 插入符号非常简单,可以通过 Qt 的强大功能重新实现:

class Caret : public QWidget {
  Q_OBJECT
  Q_PROPERTY(int period READ period WRITE setPeriod)
  QPicture m_shape;
  BasicTimer m_timer;
  int m_period;
  void updateSize() {
    auto size = m_shape.boundingRect().size();
    setFixedSize(size);
    resize(size);
  }
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() != m_timer.timerId()) return;
    if (isVisible()) hide(); else show();
  }
public:
  QPicture defaultShape(int w, int h) {
    QPicture pic;
    QPainter p(&pic);
    p.fillRect(0, 0, w, h, Qt::black);
    return pic;
  }
  Caret(QWidget * parent = 0, const QPicture & pic = defaultShape()) :
    QWidget(parent), m_shape(pic), m_period(250) {
    setAttribute(Qt::WA_TransparentForMouseEvents);
    setAttribute(Qt::WA_TranslucentBackground);
    m_timer.start(m_period);
    updateSize();
  }
  void setShape(const QPicture & pic) { 
    m_shape = pic;
    updateSize();
    update();
  }
  void setPeriod(int period) {
    if (period < 1) {
      m_timer.stop(); 
      if (m_period > 0) show();
    } else
      m_timer.start(period);
    m_period = period;
  }
  int period() const { return m_period; }
  void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE {
    auto painter = QPainter(this);
    painter.fillRect(rect(), Qt::transparent);
    painter.drawPicture(QPoint(), m_shape);
  }
};