I'm working on creating a simple editing component, and one of the things I've added so far is the ability to highlight the current line (just like the CodeEditor demo shows), as well as the ability to indent a group of lines when "Tab" is pressed. However, I have encountered a bug which I simply cannot figure out how to solve - in fact, I'm beginning to suspect that it is a but in Qt's rendering of extra text selections. Here is the simplest example I could write which reproduces this bug:

Qt Code:
  1. #include <QApplication>
  2. #include <QMainWindow>
  3. #include <QPlainTextEdit>
  4.  
  5. #include "HBPlainTextEdit.h"
  6.  
  7. class HBWindow : public QMainWindow
  8. {
  9. public:
  10. HBWindow(QWidget *p = 0, Qt::WindowFlags f = 0)
  11. : QMainWindow(p, f)
  12. {
  13. editor = new HBPlainTextEdit(this);
  14. setCentralWidget(editor);
  15. }
  16.  
  17. virtual ~HBWindow()
  18. {
  19. }
  20.  
  21. private:
  22. HBPlainTextEdit *editor;
  23. };
  24.  
  25. int main(int argc, char *argv[])
  26. {
  27. QApplication app(argc, argv);
  28.  
  29. HBWindow w;
  30. w.show();
  31.  
  32. return app.exec();
  33. }
To copy to clipboard, switch view to plain text mode 

Qt Code:
  1. #ifndef INCLUDE_HB_PLAIN_TEXT_EDIT_H
  2. #define INCLUDE_HB_PLAIN_TEXT_EDIT_H
  3.  
  4. #include <QPlainTextEdit>
  5.  
  6. class QKeyEvent;
  7.  
  8. class HBPlainTextEdit : public QPlainTextEdit
  9. {
  10. Q_OBJECT
  11.  
  12. public:
  13. HBPlainTextEdit(QWidget *p = 0);
  14. virtual ~HBPlainTextEdit();
  15.  
  16. protected:
  17. virtual void keyPressEvent(QKeyEvent *e);
  18.  
  19. private:
  20. void increaseSelectionIndent();
  21.  
  22. private slots:
  23. void highlightCurrentLine();
  24. };
  25.  
  26. #endif
To copy to clipboard, switch view to plain text mode 

Qt Code:
  1. #include "HBPlainTextEdit.h"
  2.  
  3. #include <QKeyEvent>
  4. #include <QTextBlock>
  5.  
  6. HBPlainTextEdit::HBPlainTextEdit(QWidget *p)
  7. : QPlainTextEdit(p)
  8. {
  9. QObject::connect( this, SIGNAL( cursorPositionChanged() ),
  10. this, SLOT( highlightCurrentLine() ) );
  11.  
  12. highlightCurrentLine();
  13. }
  14.  
  15. HBPlainTextEdit::~HBPlainTextEdit()
  16. {
  17. }
  18.  
  19. void HBPlainTextEdit::keyPressEvent(QKeyEvent *e)
  20. {
  21. if( (e->modifiers() == Qt::NoModifier) &&
  22. (e->key() == Qt::Key_Tab) &&
  23. textCursor().hasSelection() )
  24. {
  25. e->accept();
  26. increaseSelectionIndent();
  27. }
  28. else
  29. {
  30. QPlainTextEdit::keyPressEvent(e);
  31. }
  32. }
  33.  
  34. void HBPlainTextEdit::increaseSelectionIndent()
  35. { /* SLOT */
  36.  
  37. QTextCursor curs = textCursor();
  38.  
  39. // Do nothing if we don't have a selection.
  40.  
  41. if(!curs.hasSelection())
  42. return;
  43.  
  44. // Get the first and count of lines to indent.
  45.  
  46. int spos = curs.anchor(), epos = curs.position();
  47.  
  48. if(spos > epos)
  49. {
  50. int hold = spos;
  51. spos = epos;
  52. epos = hold;
  53. }
  54.  
  55. curs.setPosition(spos, QTextCursor::MoveAnchor);
  56. int sblock = curs.block().blockNumber();
  57.  
  58. curs.setPosition(epos, QTextCursor::MoveAnchor);
  59. int eblock = curs.block().blockNumber();
  60.  
  61. // Do the indent.
  62.  
  63. curs.setPosition(spos, QTextCursor::MoveAnchor);
  64.  
  65. curs.beginEditBlock();
  66.  
  67. for(int i = 0; i <= (eblock - sblock); ++i)
  68. {
  69. curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
  70.  
  71. curs.insertText("\t");
  72.  
  73. curs.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
  74. }
  75.  
  76. curs.endEditBlock();
  77.  
  78. // Set our cursor's selection to span all of the involved lines.
  79.  
  80. curs.setPosition(spos, QTextCursor::MoveAnchor);
  81. curs.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
  82.  
  83. while(curs.block().blockNumber() < eblock)
  84. {
  85. curs.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
  86. }
  87.  
  88. curs.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
  89.  
  90. // Done!
  91.  
  92. setTextCursor(curs);
  93.  
  94. }
  95.  
  96. void HBPlainTextEdit::highlightCurrentLine()
  97. {
  98. QList<QTextEdit::ExtraSelection> es;
  99. QTextEdit::ExtraSelection selection;
  100.  
  101. selection.format.setBackground(QColor(175, 175, 175));
  102. selection.format.setProperty(QTextFormat::FullWidthSelection, true);
  103.  
  104. selection.cursor = textCursor();
  105. selection.cursor.clearSelection();
  106.  
  107. es.append(selection);
  108. setExtraSelections(es);
  109. }
To copy to clipboard, switch view to plain text mode 

So, to reproduce this bug, the following steps should be taken after running this example program:

  1. Select two or more lines of text that have been entered
  2. Press "tab" to indent all of the lines
  3. Press CTRL+Z to undo the previous action
  4. Note that the highlight on the last line no longer extends the full width of the editor


The highlighting can be "fixed" by:

  • Adding any characters to the broken line
  • Resizing the window in any way


Does anyone have any insight into what is going wrong, or whether I should be filing a bug on Qt's bug tracker?