Qt: ByteViewText fixups.
authorGerald Combs <gerald@zing.org>
Sat, 25 Oct 2014 19:36:32 +0000 (12:36 -0700)
committerGerald Combs <gerald@wireshark.org>
Sat, 25 Oct 2014 19:39:36 +0000 (19:39 +0000)
Calculate our X coordinate using floating point arithmetic. This should
hopefully fix the wierd subpixel text shifting for some font sizes.

Add alpha blending convenience routines to ColorUtils. Use them to
create the offset colors. The button color palette wasn't working very
well under Ubuntu.

Use QFontMetricsF::lineSpacing for our line height.

Note that we don't support the BOLD gui.hex_dump_highlight_style
preference. Leave a hint for anyone wishing to do so. Comment out
related code for now.

Change-Id: Ief77c9c8d00e05560cae6ecd781bd309027f6f5a
Reviewed-on: https://code.wireshark.org/review/4927
Reviewed-by: Gerald Combs <gerald@wireshark.org>
ui/qt/byte_view_text.cpp
ui/qt/byte_view_text.h
ui/qt/color_utils.cpp
ui/qt/color_utils.h

index 8c7e097a3adeb43765c0fc8dc03a5cf6f75bb42b..809ff73c5a6ca7139b117460bd78f92b05b37267 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <epan/charsets.h>
 
+#include "color_utils.h"
 #include "wireshark_application.h"
 
 #include <QMouseEvent>
 
 // To do:
 // - Bit view
-// - Fix sub-pixel text shifting in flushOffsetFragment.
+
+// We don't obey the gui.hex_dump_highlight_style preference. If you
+// would like to add support for this you'll probably have to call
+// QPainter::drawText for each individual character.
 
 ByteViewText::ByteViewText(QWidget *parent, tvbuff_t *tvb, proto_tree *tree, QTreeWidget *tree_widget, packet_char_enc encoding) :
     QAbstractScrollArea(parent),
@@ -60,7 +64,7 @@ ByteViewText::ByteViewText(QWidget *parent, tvbuff_t *tvb, proto_tree *tree, QTr
 void ByteViewText::setEncoding(packet_char_enc encoding)
 {
     encoding_ = encoding;
-    update();
+    viewport()->update();
 }
 
 bool ByteViewText::hasDataSource(const tvbuff_t *ds_tvb) {
@@ -94,19 +98,20 @@ void ByteViewText::setFieldAppendixHighlight(int start, int end)
 
 void ByteViewText::setMonospaceFont(const QFont &mono_font)
 {
-    mono_normal_font_ = mono_font;
-    mono_bold_font_ = QFont(mono_font);
-    mono_bold_font_.setBold(true);
+    mono_font_ = mono_font;
+//    mono_bold_font_ = QFont(mono_font);
+//    mono_bold_font_.setBold(true);
 
     const QFontMetricsF fm(mono_font);
     font_width_  = fm.width('M');
+    line_spacing_ = fm.lineSpacing() + 0.5;
     one_em_ = fm.height();
-    margin_ = one_em_ / 2;
+    margin_ = fm.height() / 2;
 
     setFont(mono_font);
 
     updateScrollbars();
-    update();
+    viewport()->update();
 }
 
 void ByteViewText::paintEvent(QPaintEvent *)
@@ -124,6 +129,9 @@ void ByteViewText::paintEvent(QPaintEvent *)
     painter.fillRect(viewport()->rect(), palette().base());
 
     // Offset background
+    offset_bg_.setColor(ColorUtils::alphaBlend(palette().text(), palette().base(), 0.2));
+    offset_normal_fg_.setColor(ColorUtils::alphaBlend(palette().text(), palette().base(), 0.5));
+    offset_field_fg_.setColor(ColorUtils::alphaBlend(palette().text(), palette().base(), 0.8));
     if (show_offset_) {
         QRect offset_rect = QRect(viewport()->rect());
         offset_rect.setWidth(offsetPixels());
@@ -155,10 +163,10 @@ void ByteViewText::paintEvent(QPaintEvent *)
     // Data rows
     int widget_height = height();
     painter.save();
-    while(row_y + one_em_ < widget_height && offset < tvb_captured_length(tvb_)) {
+    while(row_y + line_spacing_ < widget_height && offset < tvb_captured_length(tvb_)) {
         drawOffsetLine(painter, offset, row_y);
         offset += row_width_;
-        row_y += one_em_;
+        row_y += line_spacing_;
     }
     painter.restore();
 }
@@ -174,7 +182,7 @@ void ByteViewText::mousePressEvent (QMouseEvent *event) {
     }
 
     QPoint pt = event->pos();
-    int byte = (verticalScrollBar()->value() + (pt.y() / one_em_)) * row_width_;
+    int byte = (verticalScrollBar()->value() + (pt.y() / line_spacing_)) * row_width_;
     int x = (horizontalScrollBar()->value() * font_width_) + pt.x();
     int col = x_pos_to_column_.value(x, -1);
 
@@ -206,6 +214,10 @@ void ByteViewText::mousePressEvent (QMouseEvent *event) {
 
 const int ByteViewText::separator_interval_ = 8; // Insert a space after this many bytes
 
+// Draw a line of byte view text for a given offset.
+// Text with different styles are split into fragments and passed to
+// flushOffsetFragment. Font character widths aren't necessarily whole
+// numbers so we track our X coordinate position using using floats.
 void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const int row_y)
 {
     if (!tvb_) {
@@ -221,14 +233,14 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
 
     QString text;
     highlight_state state = StateNormal, offset_state = StateOffsetNormal;
-    int hex_x = offsetPixels() + margin_;
-    int ascii_x = offsetPixels() + hexPixels() + margin_;
+    qreal hex_x = offsetPixels() + margin_;
+    qreal ascii_x = offsetPixels() + hexPixels() + margin_;
 
     // Hex
     if (show_hex_) {
         for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) {
             highlight_state hex_state = StateNormal;
-            bool add_space = tvb_pos % row_width_ > 0;
+            bool add_space = tvb_pos != offset;
 
             if ((tvb_pos >= f_start_ && tvb_pos < f_end_) || (tvb_pos >= fa_start_ && tvb_pos < fa_end_)) {
                 hex_state = StateField;
@@ -238,7 +250,7 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
             }
 
             if (hex_state != state) {
-                if (state != StateField && add_space) {
+                if ((state == StateNormal || (state == StateProtocol && hex_state == StateField)) && add_space) {
                     add_space = false;
                     text += ' ';
                     /* insert a space every separator_interval_ bytes */
@@ -278,7 +290,7 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
     if (show_ascii_) {
         for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) {
             highlight_state ascii_state = StateNormal;
-            bool add_space = tvb_pos % row_width_ > 0;
+            bool add_space = tvb_pos != offset;
 
             if ((tvb_pos >= f_start_ && tvb_pos < f_end_) || (tvb_pos >= fa_start_ && tvb_pos < fa_end_)) {
                 ascii_state = StateField;
@@ -288,7 +300,7 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
             }
 
             if (ascii_state != state) {
-                if (state != StateField && add_space) {
+                if ((state == StateNormal || (state == StateProtocol && ascii_state == StateField)) && add_space) {
                     add_space = false;
                     /* insert a space every separator_interval_ bytes */
                     if ((tvb_pos % separator_interval_) == 0)
@@ -326,17 +338,18 @@ void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const i
 // Draws a fragment of byte view text at the specifiec location using colors
 // for the specified state. Clears the text and returns the pixel width of the
 // drawn text.
-int ByteViewText::flushOffsetFragment(QPainter &painter, int x, int y, highlight_state state, QString &text)
+qreal ByteViewText::flushOffsetFragment(QPainter &painter, qreal x, int y, highlight_state state, QString &text)
 {
     if (text.length() < 1) {
         return 0;
     }
-    int width = text.length() * font_width_;
+    QFontMetricsF fm(mono_font_);
+    qreal width = fm.width(text);
     // Background
     if (state == StateField) {
-        painter.fillRect(x, y, width, one_em_, palette().highlight());
+        painter.fillRect(QRectF(x, y, width, line_spacing_), palette().highlight());
     } else if (state == StateProtocol) {
-        painter.fillRect(x, y, width, one_em_, palette().button());
+        painter.fillRect(QRectF(x, y, width, line_spacing_), palette().button());
     }
 
     // Text
@@ -353,15 +366,15 @@ int ByteViewText::flushOffsetFragment(QPainter &painter, int x, int y, highlight
         text_brush = palette().buttonText();
         break;
     case StateOffsetNormal:
-        text_brush = palette().dark();
+        text_brush = offset_normal_fg_;
         break;
     case StateOffsetField:
-        text_brush = palette().buttonText();
+        text_brush = offset_field_fg_;
         break;
     }
 
     painter.setPen(QPen(text_brush.color()));
-    painter.drawText(x, y, width, one_em_, Qt::AlignTop, text);
+    painter.drawText(QRectF(x, y, width, line_spacing_), Qt::AlignTop, text);
     text.clear();
     return width;
 }
@@ -417,7 +430,7 @@ void ByteViewText::updateScrollbars()
     if (tvb_) {
     }
 
-    qint64 maxval = length / row_width_ + ((length % row_width_) ? 1 : 0) - viewport()->height() / one_em_;
+    qint64 maxval = length / row_width_ + ((length % row_width_) ? 1 : 0) - viewport()->height() / line_spacing_;
 
     verticalScrollBar()->setRange(0, qMax((qint64)0, maxval));
     horizontalScrollBar()->setRange(0, qMax(0, static_cast<int>((totalPixels() - viewport()->width()) / font_width_)));
index 24686e8ce852af7d8096d786a4d964d1f5067236..e40c256ec3580e955718dbe94ec1f3f2dfd9c0b8 100644 (file)
@@ -72,7 +72,7 @@ private:
     } highlight_state;
 
     void drawOffsetLine(QPainter &painter, const guint offset, const int row_y);
-    int flushOffsetFragment(QPainter &painter, int x, int y, highlight_state state, QString &text);
+    qreal flushOffsetFragment(QPainter &painter, qreal x, int y, highlight_state state, QString &text);
     void scrollToByte(int byte);
     int offsetChars();
     int offsetPixels();
@@ -85,8 +85,13 @@ private:
     tvbuff_t *tvb_;
     proto_tree *proto_tree_;
     QTreeWidget *tree_widget_;
-    QFont mono_normal_font_;
-    QFont mono_bold_font_;
+
+    // Fonts and colors
+    QFont mono_font_;
+//    QFont mono_bold_font_;
+    QBrush offset_bg_;
+    QBrush offset_normal_fg_;
+    QBrush offset_field_fg_;
 
     gboolean bold_highlight_;
 
@@ -105,6 +110,7 @@ private:
     guint row_width_;           // Number of bytes per line
     int one_em_;                // Font character height
     qreal font_width_;          // Font character width
+    int line_spacing_;          // Font line spacing
     int margin_;                // Text margin
 
     // Data selection
index 0603a26b98bd431d23d6829a7f86c28f89304841..4210d297341719ff0fa2b6f96aa7d7edd8eaad42 100644 (file)
@@ -60,6 +60,26 @@ QColor ColorUtils::fromColorT(color_t color)
     return fromColorT(&color);
 }
 
+QRgb ColorUtils::alphaBlend(const QColor &color1, const QColor &color2, qreal alpha)
+{
+    alpha = qBound(0.0, alpha, 1.0);
+
+    int r1 = color1.red() * alpha;
+    int g1 = color1.green() * alpha;
+    int b1 = color1.blue() * alpha;
+    int r2 = color2.red() * (1 - alpha);
+    int g2 = color2.green() * (1 - alpha);
+    int b2 = color2.blue() * (1 - alpha);
+
+    QColor alpha_color(r1 + r2, g1 + g2, b1 + b2);
+    return alpha_color.rgb();
+}
+
+QRgb ColorUtils::alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha)
+{
+    return alphaBlend(brush1.color(), brush2.color(), alpha);
+}
+
 /*
  * Editor modelines
  *
index 484001ddf4b636329db45474c9902c271342d3b9..abbd4497a235875a336ff34ef7dc7012cc4e242d 100644 (file)
@@ -28,8 +28,9 @@
 
 #include "color.h"
 
-#include <QObject>
+#include <QBrush>
 #include <QColor>
+#include <QObject>
 
 class ColorUtils : public QObject
 {
@@ -39,6 +40,8 @@ public:
 
     static QColor fromColorT(color_t *color);
     static QColor fromColorT(color_t color);
+    static QRgb alphaBlend(const QColor &color1, const QColor &color2, qreal alpha);
+    static QRgb alphaBlend(const QBrush &brush1, const QBrush &brush2, qreal alpha);
 
 signals: