PDA

View Full Version : Custom LineEdit - need help!



ArlexBee-871RBO
20th May 2010, 07:27
Greetings. Instead of using QStyle, I have sub-classed QLineEdit and reimplemented the paintEvent function (don't ask why) for custom look. It looks almost finished, but I have a problem with the cursor. The position of the cursor is not being rendered correctly, and I've tried different things without success. If I directly draw the cursor position then it gets better, but it still starts to show problems when the line is filled with more text. It gets much worse with QTextLayout, and I don't find the documentation very helpful.

Here is what I've got so far. Much of it I got from Qt after studying the source code. I've intentionally given the LineEdit a fixed size to make sure the alignment is as it should be.



#include <QtGui>
#include <QtCore>

class Edit : public QLineEdit{

public:
Edit(const QString &t, QWidget *pParent = 0) : QLineEdit(t, pParent){
font = QFont("Sans");
font.setPixelSize(12);
setFixedSize(640, 220);
setTextMargins(4, 0, 4, 0);
}

protected:
void paintEvent(QPaintEvent *pEvent){

QPainter painter(this);
QRect r = rect();

//frame
int x2 = r.width() - 1, y2 = r.height() - 1;
painter.fillRect(r, QBrush(QColor(79, 79, 104, 255)));
painter.setPen(QPen(QColor(64, 64, 64, 255), 1));
painter.drawLine(0, 0, x2, 0);
painter.drawLine(0, 0, 0, y2);
painter.setPen(QPen(QColor(92, 92, 92, 255), 1));
painter.drawLine(x2, y2, 1, y2);
painter.drawLine(x2, y2, x2, 1);
painter.setPen(QPen(QColor(19, 19, 19, 255), 1));
painter.drawRect(QRect(1, 1, r.width() - 3, r.height() - 3));

//set clipping
int lm, tm, rm, bm;
getTextMargins(&lm, &tm, &rm, &bm);
r.setX(r.x() + lm);
r.setY(r.y() + tm);
r.setRight(r.right() - rm);
r.setBottom(r.bottom() - bm);
painter.setClipRect(r);

painter.setPen(QPen(QColor(196, 196, 196, 255), 1));
QFontMetrics fm(font);
QTextLayout tl(text(), font, this);
tl.beginLayout();
QTextLine line = tl.createLine();
if( !line.isValid() ){ return; }
tl.endLayout();
QRect lineRect(r.x(), r.y() + (r.height() - qRound(line.height()) + 1) / 2,
r.width() - 1, qRound(line.height()));
//painter.drawRect(lineRect);
int ccc = cursorPosition();
int cix = qRound(line.cursorToX(&ccc));
int minLB = qMax(0, -fm.minLeftBearing());
int minRB = qMax(0, -fm.minRightBearing());
int widthUsed = qRound(line.naturalTextWidth()) + 1 + minRB;
int hscroll;

if( (minLB + widthUsed) <= lineRect.width() ){
hscroll = 0;
hscroll -= minLB;
}else if( cix - hscroll >= lineRect.width() ){
hscroll = cix - lineRect.width() + 1;
}else if( cix - hscroll < 0 && hscroll < widthUsed ){
hscroll = cix;
}else if( widthUsed - hscroll < lineRect.width() ){
hscroll = widthUsed - lineRect.width() + 1;
}
QPoint topLeft = lineRect.topLeft() -
QPoint(hscroll, line.ascent() - fm.ascent());

QVector<QTextLayout::FormatRange> selections;
QTextLayout::FormatRange o;
if( hasSelectedText() ){
o.start = selectionStart();
o.length = selectedText().length();
o.format.setBackground(QColor(88, 101, 134, 255));
o.format.setForeground(QColor(196, 196, 196, 255));
selections.append(o);
}
tl.draw(&painter, topLeft, selections);
painter.setPen(QPen(QColor(200, 0, 0, 255), 2));
//painter.drawLine(cix, lineRect.y(), cix, lineRect.y() + lineRect.height());
tl.drawCursor(&painter, topLeft, cix, 2);
}

private:
QFont font;
};

int main(int argc, char *argv[]){

QApplication a(argc, argv);

QWidget *w = new QWidget;
w->setPalette(QPalette(QColor(77, 77, 77, 255)));
QHBoxLayout *l = new QHBoxLayout(w);
Edit *e1 = new Edit(QString("test1"));
QLineEdit *e2 = new QLineEdit(QString("test2"));
e2->setFixedSize(640, 220);
l->addWidget(e1);
l->addWidget(e2);
w->show();

return a.exec();
}


Any idea why QTextLayout doesn't render the cursor properly?

wysota
20th May 2010, 09:33
Could you provide some info on what you get and what you want? An image would be nice...

ArlexBee-871RBO
20th May 2010, 10:35
It would be difficult with a picture, that's why I wrote a small program, but the problem is this: The cursor (the red vertical line) isn't rendered where you click the text. Also, trying to move the cursor with the left and right keys makes the cursor jump several characters and it never goes where it's supposed to.

It's as if QLineEdit::cursorPosition() doesn't return the correct value, but it does as far as I can tell. For some reason QTextLayout::drawCursor() doesn't render the cursor properly.

wysota
20th May 2010, 10:50
Maybe the painter is prepared incorrectly?

ArlexBee-871RBO
20th May 2010, 11:03
If you uncomment the last commented-out line, the painter draws a vertical line where the cursor should go. That's much more accurate than what the QTextLayout::draw() renders, but it's still off. Somehow there is a miscalculation.

wysota
20th May 2010, 11:11
Check the implementation of drawCursor(), maybe it does some painter transformations prior to drawing the cursor. Maybe this will fix the difference between your line and drawCursor(). If the line itself is at a wrong position then I'd assume your earlier calculations don't take some aspect into consideration. Check the source code for QLineEdit again and see if you didn't miss something.

ArlexBee-871RBO
21st May 2010, 04:43
pfff... finally, I fixed it. I had to reimplement the QLineEdit::mousePressEvent() and QLineEdit::mouseMoveEvent() methods to calculate the cursor position and call setCursorPosition().

steno
15th September 2010, 16:55
Could you post your code for the mouse events?

estanisgeyer
15th September 2010, 17:10
Send image for us.