Make URLs (and channel names!) in the topic widget clickable again
authorManuel Nickschas <sputnick@quassel-irc.org>
Tue, 25 Aug 2009 19:48:26 +0000 (21:48 +0200)
committerManuel Nickschas <sputnick@quassel-irc.org>
Tue, 25 Aug 2009 19:50:50 +0000 (21:50 +0200)
This time properly, including on-hover underline and proper coloring.
Also, we now use the widget font rather than the ChatView font for the topic.

src/qtui/topicwidget.cpp
src/qtui/topicwidget.h
src/uisupport/styledlabel.cpp
src/uisupport/styledlabel.h

index 8a49d27..2f8f5fa 100644 (file)
@@ -30,8 +30,9 @@ TopicWidget::TopicWidget(QWidget *parent)
   ui.setupUi(this);
   ui.topicEditButton->setIcon(SmallIcon("edit-rename"));
   ui.topicLineEdit->setWordWrapEnabled(true);
-
   ui.topicLineEdit->installEventFilter(this);
+
+  connect(ui.topicLabel, SIGNAL(clickableActivated(Clickable)), SLOT(clickableActivated(Clickable)));
 }
 
 void TopicWidget::currentChanged(const QModelIndex &current, const QModelIndex &previous) {
@@ -56,6 +57,11 @@ void TopicWidget::setTopic(const QString &newtopic) {
   switchPlain();
 }
 
+void TopicWidget::clickableActivated(const Clickable &click) {
+  NetworkId networkId = selectionModel()->currentIndex().data(NetworkModel::NetworkIdRole).value<NetworkId>();
+  click.activate(networkId, _topic);
+}
+
 void TopicWidget::on_topicLineEdit_textEntered() {
   QModelIndex currentIdx = currentIndex();
   if(currentIdx.isValid() && currentIdx.data(NetworkModel::BufferTypeRole) == BufferInfo::ChannelBuffer) {
index 76e68f1..18eae86 100644 (file)
@@ -44,6 +44,7 @@ private slots:
   void on_topicEditButton_clicked();
   void switchEditable();
   void switchPlain();
+  void clickableActivated(const Clickable &);
 
 private:
   Ui::TopicWidget ui;
index 13690df..372b9bb 100644 (file)
@@ -32,6 +32,8 @@ StyledLabel::StyledLabel(QWidget *parent)
   _alignment(Qt::AlignVCenter|Qt::AlignLeft),
   _toolTipEnabled(true)
 {
+  setMouseTracking(true);
+
   QTextOption opt = _layout.textOption();
   opt.setWrapMode(_wrapMode);
   opt.setAlignment(_alignment);
@@ -89,10 +91,31 @@ void StyledLabel::setText(const QString &text) {
   UiStyle::StyledString sstr = style->styleString(style->mircToInternal(text), UiStyle::PlainMsg);
   QList<QTextLayout::FormatRange> layoutList = style->toTextLayoutList(sstr.formatList, sstr.plainText.length(), 0);
 
+  // Use default font rather than the style's
+  QTextLayout::FormatRange fmtRange;
+  fmtRange.format.setFont(font());
+  fmtRange.start = 0;
+  fmtRange.length = sstr.plainText.length();
+  layoutList << fmtRange;
+
+  // Mark URLs
+  _clickables = ClickableList::fromString(sstr.plainText);
+  foreach(Clickable click, _clickables) {
+    if(click.type() == Clickable::Url) {
+      QTextLayout::FormatRange range;
+      range.start = click.start();
+      range.length = click.length();
+      range.format.setForeground(palette().link());
+      layoutList << range;
+    }
+  }
+
   _layout.setText(sstr.plainText);
   _layout.setAdditionalFormats(layoutList);
 
   layout();
+
+  endHoverMode();
 }
 
 void StyledLabel::updateToolTip() {
@@ -126,5 +149,62 @@ void StyledLabel::paintEvent(QPaintEvent *) {
   QPainter painter(this);
 
   qreal y = (frameRect().height() - _layout.boundingRect().height()) / 2;
-  _layout.draw(&painter, QPointF(0, y), QVector<QTextLayout::FormatRange>());
+  _layout.draw(&painter, QPointF(0, y), _extraLayoutList);
+}
+
+int StyledLabel::posToCursor(const QPointF &pos) {
+  if(pos.y() < 0 || pos.y() > height())
+    return -1;
+
+  for(int l = _layout.lineCount() - 1; l >= 0; l--) {
+    QTextLine line = _layout.lineAt(l);
+    if(pos.y() >= line.y()) {
+      return line.xToCursor(pos.x(), QTextLine::CursorOnCharacter);
+    }
+  }
+  return -1;
+}
+
+void StyledLabel::mouseMoveEvent(QMouseEvent *event) {
+  if(event->buttons() == Qt::NoButton) {
+    Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
+    if(click.isValid())
+      setHoverMode(click.start(), click.length());
+    else
+      endHoverMode();
+  }
+}
+
+void StyledLabel::leaveEvent(QEvent *) {
+  endHoverMode();
 }
+
+void StyledLabel::mousePressEvent(QMouseEvent *event) {
+  if(event->button() == Qt::LeftButton) {
+    Clickable click = _clickables.atCursorPos(posToCursor(event->posF()));
+    if(click.isValid())
+      emit clickableActivated(click);
+  }
+}
+
+void StyledLabel::setHoverMode(int start, int length) {
+  if(_extraLayoutList.count() >= 1 && _extraLayoutList.first().start == start && _extraLayoutList.first().length == length)
+    return;
+
+  QTextLayout::FormatRange range;
+  range.start = start;
+  range.length = length;
+  range.format.setFontUnderline(true);
+  _extraLayoutList.clear();
+  _extraLayoutList << range;
+
+  setCursor(Qt::PointingHandCursor);
+  update();
+}
+
+void StyledLabel::endHoverMode() {
+  _extraLayoutList.clear();
+  setCursor(Qt::ArrowCursor);
+  update();
+}
+
index 4eefb6a..de412a8 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <QFrame>
 
+#include "clickable.h"
 #include "uistyle.h"
 
 class StyledLabel : public QFrame {
@@ -45,27 +46,35 @@ public:
   inline bool toolTipEnabled() const { return _toolTipEnabled; }
   void setToolTipEnabled(bool);
 
+signals:
+  void clickableActivated(const Clickable &click);
+
 protected:
   virtual void paintEvent(QPaintEvent *event);
   virtual void resizeEvent(QResizeEvent *event);
+  virtual void leaveEvent(QEvent *);
+  virtual void mouseMoveEvent(QMouseEvent *event);
+  virtual void mousePressEvent(QMouseEvent *event);
 
-  //void mouseMoveEvent(QMouseEvent *event);
-  //void mousePressEvent(QMouseEvent *event);
-  //void mouseReleaseEvent(QMouseEvent *event);
-  //void mouseDoubleClickEvent(QMouseEvent *event);
+  int posToCursor(const QPointF &pos);
 
 private:
   QSize _sizeHint;
   QTextOption::WrapMode _wrapMode;
   Qt::Alignment _alignment;
   QTextLayout _layout;
+  ClickableList _clickables;
   bool _toolTipEnabled;
 
   QList<QTextLayout::FormatRange> _layoutList;
+  QVector<QTextLayout::FormatRange> _extraLayoutList;
 
   void layout();
   void updateSizeHint();
   void updateToolTip();
+
+  void setHoverMode(int start, int length);
+  void endHoverMode();
 };
 
 #endif