QPlainTextEdit left margin for text
-
Hello,
I have a QPlainTextEdit component as a text editor. It has a custom paintEvent which augments the visual appearance of the editor in a certain way. My problem is that I would like to have a few pixels margin between the text and the left border of the editor component, but I'm not able to do this in a satisfactory way. (By default the text lines are horizontally aligned tightly to the left side of the widget.)
Setting a left margin for the component using something like setStyleSheet("margin-left:8px") does have the desired effect, but alas the margin created this way isn't available for paintEvent to draw onto. I need the paintEvent to be able to draw onto the margin, so that the style added by the custom paint event carries over to the margin as well. Given this constraint the default margin seems not to be an option.
The only solution I can come up with is to introduce another blank canvas type of widget with a custom paintEvent to the left side of the QPlainTextEdit widget. This feels a little cumbersome since all I want to do is to move the text to the right for a few pixels, so I thought I would ask if someone can come up with better ideas.
(Also, adding the extra padding widget feels a bit non-optimal because the custom paint routine iterates though the visible text blocks in the document, in a way which might be potentially slow. Responsiveness is of utmost importance so I'd rather not add anything that could affect performance negatively if I can help it.)
-
Hi,
Have look at setViewportMarginsWhat you want to do resemble very much like in this example:
Code Editor -
In that example they are using a separate widget with a custom paint event for the margin area. I guess I'm not completely comfortable with sub-widgets that aren't part of any layout, but copying what they did there seems to work, at least to a degree.
If this is the best solution available then I would like to make sure the paint event of the master widget (QPlainTextEdit) always happens first. (This is because I then can cache the results of the potentially expensive document iterating and use the same data for painting the sub-widget.) I'm not sure if there is a good way to guarantee this, other than calling repaint() on the sub-widget from the master paint event and keeping updates disabled for it at all other times.
I didn't implement everything yet but it seems this could potentially work adequately, so thanks for the suggestion.
-
Here's the code. "TextBuffer" derives from QPlainTextEdit. The last 3 lines are today's experiment with the margin sub-widget. I don't know what "start position" means, but if you could e.g. transpose the coordinates that QPlainTextEdit::paintEvent() uses when drawing the content, that would probably accomplish the goal
void TextBuffer::paintEvent(QPaintEvent* e) { QPainter painter(viewport()); QBrush brush0(theme.shadow); QBrush brush1(theme.ether); int w=painter.viewport().width(); QTextBlock block=firstVisibleBlock(); QRectF p=blockBoundingGeometry(block).translated(contentOffset()); int y1=(int)p.top(); int y0=y1-rowHgt; int bottom=y1+painter.viewport().height(); int d=rowHgt>>2; while(y1<bottom) { bool full=true; if(!block.isValid() || !block.isVisible()) { y1=bottom; } else if(block.userState()!=0x10000) { full=block.length()>1; } if(full && y1>y0) { y0+=d; painter.fillRect(0 ,y0,1,1,brush0); painter.fillRect(w-1,y0,1,1,brush0); ++y0; painter.fillRect(0, y0,1,1,brush1); painter.fillRect(w-1,y0,1,1,brush1); painter.fillRect(1,y0,w-2,1,brush0); ++y0; int y2=y1-1-d; painter.fillRect(0 ,y2,1,1,brush1); painter.fillRect(w-1,y2,1,1,brush1); // painter.fillRect(0,y0,w,y2-y0,brush1); } y1+=rowHgt; if(full) { y0=y1; } else if(y1>=bottom) { y0+=d; painter.fillRect(0 ,y0,1,1,brush0); painter.fillRect(w-1,y0,1,1,brush0); ++y0; painter.fillRect(0, y0,1,1,brush1); painter.fillRect(w-1,y0,1,1,brush1); painter.fillRect(1,y0,w-2,1,brush0); ++y0; painter.fillRect(0,y0,w,bottom-y0,brush1); } block=block.next(); } QPlainTextEdit::paintEvent(e); margin->setUpdatesEnabled(true); margin->repaint(); margin->setUpdatesEnabled(false); }
-
QPoint p; /* p is the point of text rect left bottom corner. */ painter.setPen(color); painter.drawText(p.x() + off, p.y() + off, text);
you paint text by yourself without calling QPlainTextEdit::paintEvent(e);
p has to be calculated properly and p.x() can be set with the margin. -
The text display is using QSyntaxHighlighter, so it feels quite non-trivial to implement the text rendering without calling paintEvent in the parent class.
-
painter.drawText() draws the text using the selected pen color, doesn't it? I vaguely remember trying to figure out how to invoke QSyntaxHighlighter manually (i.e. outside of QPlainTextEdit::paintEvent()) but I was unable to. So I'm not sure how to break each line into substrings that can have different colors according to the attached highlighter.
-
painter.drawText() draws the text using the selected pen color, doesn't it? I vaguely remember trying to figure out how to invoke QSyntaxHighlighter manually (i.e. outside of QPlainTextEdit::paintEvent()) but I was unable to. So I'm not sure how to break each line into substrings that can have different colors according to the attached highlighter.
@Lasse It is the color you choose. Try it out and you learn something new. Use the font size to find the bounding box of your text
QRect text_rect = this->fontMetrics().tightBoundingRect( text );
and then set proper x0 and y0 for the upper corner point of text_rect. -
Drawing the QPlainTextEdit by hand seems very hard without calling the parent class' paintEvent.
For example, if I move the text to the right in the paint method, the system generating the paint events doesn't know it and the update regions are off. I now have to draw the text cursor manually. QPlainTextEdit::cursorRect() returns the wrong position for the cursor because that also doesn't know where anything is anymore. Obviously I can translate the cursor rectangle before drawing it, but if I do that the cursor will leave visible trails on the screen, again, because the update regions are off. I do not even know if the text cursor should be drawn or not, since sometimes its blinking and therefore should not be drawn. At a glance the blink status information doesn't seem to be available through the QPlainTextEdit API.
These are just some of the problems with this approach. Other problems include refactoring and detaching the mentioned QHighlighter functionality from QPlainTextEdit, which is responsible for the colors of the visible glyphs on the screen. Caching glyphs in QPixmaps instead of drawing directly on the surface using drawText() showed some promise in terms of performance, but I'm not convinced of this either. I didn't even start figuring out how to determine if a glyph is selected and thus should be drawn with the different, highlighted background.
Seems a bit overkill don't you think. All I wanted to do was move the text to the right a few pixels.