]> granicus.if.org Git - transmission/commitdiff
Torrent properties dialog improvements
authorMike Gelfand <mikedld@mikedld.com>
Mon, 15 Jun 2015 21:07:46 +0000 (21:07 +0000)
committerMike Gelfand <mikedld@mikedld.com>
Mon, 15 Jun 2015 21:07:46 +0000 (21:07 +0000)
Simplify DND checkboxes drawing, this also fixes incorrect drawing on
Mac when file tree widget is inactive.
Do better job calculating column widths for file tree to avoid ellipsis.
Fix file tree sorting order for size and priority columns.
Change key to toggle priorities to Shift+Space instead of Enter/Return
to avoid conflicts with name editing and default button handling.
Fix selected tracker item background drawing in certain cases.

qt/DetailsDialog.cc
qt/DetailsDialog.ui
qt/FileTreeDelegate.cc
qt/FileTreeItem.cc
qt/FileTreeItem.h
qt/FileTreeModel.h
qt/FileTreeView.cc
qt/FileTreeView.h
qt/TrackerDelegate.cc
qt/Utils.cc
qt/Utils.h

index 323898e926e7842fd44c1faa9257087cac8fa4d0..bf35e0d6084e019fdee23a00e880d7a6779b86dd 100644 (file)
@@ -71,13 +71,14 @@ namespace
   };
 
   int
-  measureViewItem (QAbstractItemView * view, const QString& text)
+  measureViewItem (QTreeWidget * view, int column, const QString& text)
   {
-    QStyleOptionViewItemV4 option;
-    option.features = QStyleOptionViewItemV2::HasDisplay;
-    option.text = text;
-    return view->style ()->sizeFromContents (QStyle::CT_ItemViewItem, &option,
-      QSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).width ();
+    const QTreeWidgetItem * headerItem = view->headerItem ();
+
+    const int itemWidth = Utils::measureViewItem (view, text);
+    const int headerWidth = Utils::measureHeaderItem (view->header (), headerItem->text (column));
+
+    return std::max (itemWidth, headerWidth);
   }
 }
 
@@ -1245,11 +1246,11 @@ DetailsDialog::initPeersTab ()
   ui.peersView->sortByColumn (COL_ADDRESS, Qt::AscendingOrder);
 
   ui.peersView->setColumnWidth (COL_LOCK, 20);
-  ui.peersView->setColumnWidth (COL_UP, measureViewItem (ui.peersView, QLatin1String ("1024 MiB/s")));
-  ui.peersView->setColumnWidth (COL_DOWN, measureViewItem (ui.peersView, QLatin1String ("1024 MiB/s")));
-  ui.peersView->setColumnWidth (COL_PERCENT, measureViewItem (ui.peersView, QLatin1String ("100%")));
-  ui.peersView->setColumnWidth (COL_STATUS, measureViewItem (ui.peersView, QLatin1String ("ODUK?EXI")));
-  ui.peersView->setColumnWidth (COL_ADDRESS, measureViewItem (ui.peersView, QLatin1String ("888.888.888.888")));
+  ui.peersView->setColumnWidth (COL_UP, measureViewItem (ui.peersView, COL_UP, QLatin1String ("1024 MiB/s")));
+  ui.peersView->setColumnWidth (COL_DOWN, measureViewItem (ui.peersView, COL_DOWN, QLatin1String ("1024 MiB/s")));
+  ui.peersView->setColumnWidth (COL_PERCENT, measureViewItem (ui.peersView, COL_PERCENT, QLatin1String ("100%")));
+  ui.peersView->setColumnWidth (COL_STATUS, measureViewItem (ui.peersView, COL_STATUS, QLatin1String ("ODUK?EXI")));
+  ui.peersView->setColumnWidth (COL_ADDRESS, measureViewItem (ui.peersView, COL_ADDRESS, QLatin1String ("888.888.888.888")));
 }
 
 /***
index f0cb906415f8f54afcb51851ad7a58998005b866..c0e6425ca8c2eddead01298dbe9f6e1d8df30a3f 100644 (file)
       <layout class="QVBoxLayout" name="filesTabLayout">
        <item>
         <widget class="FileTreeView" name="filesView">
+         <property name="horizontalScrollBarPolicy">
+          <enum>Qt::ScrollBarAlwaysOff</enum>
+         </property>
+         <property name="alternatingRowColors">
+          <bool>true</bool>
+         </property>
+         <property name="selectionMode">
+          <enum>QAbstractItemView::ExtendedSelection</enum>
+         </property>
          <property name="verticalScrollMode">
           <enum>QAbstractItemView::ScrollPerPixel</enum>
          </property>
+         <property name="sortingEnabled">
+          <bool>true</bool>
+         </property>
         </widget>
        </item>
       </layout>
index a08f2c2fa38c18a50ecd801c54db7758d32039ba..b14f9bdf61179e9ffd765f7315612e458b24d0f0 100644 (file)
@@ -57,7 +57,7 @@ FileTreeDelegate::paint (QPainter                    * painter,
       p.state = option.state | QStyle::State_Small;
       p.direction = qApp->layoutDirection();
       p.rect = option.rect;
-      p.rect.setSize (QSize(option.rect.width()-2, option.rect.height()-8));
+      p.rect.setSize (QSize(option.rect.width() - 4, option.rect.height() - 8));
       p.rect.moveCenter (option.rect.center());
       p.fontMetrics = qApp->fontMetrics();
       p.minimum = 0;
@@ -70,19 +70,11 @@ FileTreeDelegate::paint (QPainter                    * painter,
     }
   else if(column == FileTreeModel::COL_WANTED)
     {
-      QStyleOptionButton o;
-      o.state = option.state;
-      o.direction = qApp->layoutDirection();
-      o.rect.setSize (QSize(20, option.rect.height()));
-      o.rect.moveCenter (option.rect.center());
-      o.fontMetrics = qApp->fontMetrics();
-      switch (index.data().toInt())
-        {
-          case Qt::Unchecked: o.state |= QStyle::State_Off; break;
-          case Qt::Checked:   o.state |= QStyle::State_On; break;
-          default:            o.state |= QStyle::State_NoChange;break;
-        }
-      style->drawControl (QStyle::CE_CheckBox, &o, painter);
+      QStyleOptionViewItemV4 vi (option);
+      vi.features |= QStyleOptionViewItemV4::HasCheckIndicator;
+      QRect checkRect = style->subElementRect (QStyle::SE_ItemViewItemCheckIndicator, &vi, nullptr);
+      checkRect.moveCenter (option.rect.center ());
+      drawCheck (painter, vi, checkRect, static_cast<Qt::CheckState> (index.data ().toInt ()));
     }
 
   QItemDelegate::drawFocus (painter, option, option.rect);
index 06a9b5d3edc20433f3c5a17b57db332cf1da42b4..7c52c81357ecb2f186b84c4727ebdba4c1d7749d 100644 (file)
@@ -107,29 +107,35 @@ FileTreeItem::data (int column, int role) const
     {
       value = Qt::AlignRight + Qt::AlignVCenter;
     }
-  else if (role == Qt::DisplayRole)
+  else if (role == Qt::DisplayRole || role == FileTreeModel::SortRole)
     {
       switch(column)
-       {
-         case FileTreeModel::COL_NAME:
-           value.setValue (name());
-           break;
-
-         case FileTreeModel::COL_SIZE:
-           value.setValue (sizeString() + QLatin1String ("  "));
-           break;
-
-         case FileTreeModel::COL_PROGRESS:
-           value.setValue (progress());
-           break;
-
-         case FileTreeModel::COL_WANTED:
-           value.setValue (isSubtreeWanted());
-           break;
-
-         case FileTreeModel::COL_PRIORITY:
-           value.setValue (priorityString());
-           break;
+        {
+          case FileTreeModel::COL_NAME:
+            value.setValue (name());
+            break;
+
+          case FileTreeModel::COL_SIZE:
+            if (role == Qt::DisplayRole)
+              value.setValue (sizeString());
+            else
+              value.setValue (size ());
+            break;
+
+          case FileTreeModel::COL_PROGRESS:
+            value.setValue (progress());
+            break;
+
+          case FileTreeModel::COL_WANTED:
+            value.setValue (isSubtreeWanted());
+            break;
+
+          case FileTreeModel::COL_PRIORITY:
+            if (role == Qt::DisplayRole)
+              value.setValue (priorityString());
+            else
+              value.setValue (priority ());
+            break;
         }
     }
   else if (role == Qt::DecorationRole && column == FileTreeModel::COL_NAME)
@@ -172,21 +178,19 @@ FileTreeItem::progress () const
 QString
 FileTreeItem::sizeString () const
 {
-  QString str;
+  return Formatter::sizeToString (size ());
+}
 
+uint64_t
+FileTreeItem::size () const
+{
   if (myChildren.isEmpty())
-    {
-      str = Formatter::sizeToString (myTotalSize);
-    }
-  else
-    {
-      uint64_t have = 0;
-      uint64_t total = 0;
-      getSubtreeWantedSize (have, total);
-      str = Formatter::sizeToString (total);
-    }
+    return myTotalSize;
 
-  return str;
+  uint64_t have = 0;
+  uint64_t total = 0;
+  getSubtreeWantedSize (have, total);
+  return total;
 }
 
 std::pair<int,int>
index e4a7b74d3105940474d21d740fe37b8e3879ec48..420862f89f067ba23a5fb66e6aa65ba34364ddc1 100644 (file)
@@ -22,6 +22,7 @@
 class FileTreeItem
 {
     Q_DECLARE_TR_FUNCTIONS (FileTreeItem)
+    Q_DISABLE_COPY (FileTreeItem)
 
   public:
     FileTreeItem (const QString& name = QString (), int fileIndex = -1, uint64_t size = 0):
@@ -69,6 +70,7 @@ class FileTreeItem
     void getSubtreeWantedSize (uint64_t& have, uint64_t& total) const;
     double progress () const;
     int priority () const;
+    uint64_t size () const;
     int isSubtreeWanted () const;
     const QHash<QString,int>& getMyChildRows();
 
index 92fd4714ce9c1ebd8ae973c7489593e238089007..9f122209e0fac064fe2423b41f2f210fb80a6859 100644 (file)
@@ -38,6 +38,11 @@ class FileTreeModel: public QAbstractItemModel
       NUM_COLUMNS
     };
 
+    enum Role
+    {
+      SortRole = Qt::UserRole
+    };
+
   public:
     FileTreeModel (QObject * parent = nullptr, bool isEditable = true);
     virtual ~FileTreeModel ();
index 17f0620ca0ab949197f07a5c58add94ee757b5e6..6dfed35fac2dbf3ce5b2d922eec07bca6d374d50 100644 (file)
 #include <QSortFilterProxyModel>
 
 #include "FileTreeDelegate.h"
+#include "FileTreeItem.h"
 #include "FileTreeModel.h"
 #include "FileTreeView.h"
+#include "Formatter.h"
+#include "Utils.h"
 
 FileTreeView::FileTreeView (QWidget * parent, bool isEditable):
   QTreeView (parent),
@@ -23,27 +26,16 @@ FileTreeView::FileTreeView (QWidget * parent, bool isEditable):
   myProxy (new QSortFilterProxyModel (this)),
   myDelegate (new FileTreeDelegate (this))
 {
-  setSortingEnabled (true);
-  setAlternatingRowColors (true);
-  setSelectionBehavior (QAbstractItemView::SelectRows);
-  setSelectionMode (QAbstractItemView::ExtendedSelection);
   myProxy->setSourceModel (myModel);
+  myProxy->setSortRole (FileTreeModel::SortRole);
+  myProxy->setSortCaseSensitivity (Qt::CaseInsensitive);
+
   setModel (myProxy);
   setItemDelegate (myDelegate);
-  setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
   sortByColumn (FileTreeModel::COL_NAME, Qt::AscendingOrder);
-  installEventFilter (this);
 
   for (int i=0; i<FileTreeModel::NUM_COLUMNS; ++i)
-    {
-      setColumnHidden (i, (i<FileTreeModel::FIRST_VISIBLE_COLUMN) || (FileTreeModel::LAST_VISIBLE_COLUMN<i));
-
-#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
-      header()->setResizeMode(i, QHeaderView::Interactive);
-#else
-      header()->setSectionResizeMode(i, QHeaderView::Interactive);
-#endif
-    }
+    setColumnHidden (i, (i<FileTreeModel::FIRST_VISIBLE_COLUMN) || (FileTreeModel::LAST_VISIBLE_COLUMN<i));
 
   connect (this, SIGNAL(clicked(QModelIndex)),
            this, SLOT(onClicked(QModelIndex)));
@@ -88,58 +80,90 @@ FileTreeView::onOpenRequested (const QString& path)
   emit openRequested (path);
 }
 
-bool
-FileTreeView::eventFilter (QObject * o, QEvent * event)
+void
+FileTreeView::resizeEvent (QResizeEvent * event)
 {
-  // this is kind of a hack to get the last three columns be the
+  QTreeView::resizeEvent (event);
+
+  // this is kind of a hack to get the last four columns be the
   // right size, and to have the filename column use whatever
   // space is left over...
-  if ((o == this) && (event->type() == QEvent::Resize))
+
+  int left = event->size ().width () - 1;
+  for (int column = FileTreeModel::FIRST_VISIBLE_COLUMN; column <= FileTreeModel::LAST_VISIBLE_COLUMN; ++column)
     {
-      QResizeEvent * r = static_cast<QResizeEvent*> (event);
-      int left = r->size().width();
-      const QFontMetrics fontMetrics(font());
-      for (int column=FileTreeModel::FIRST_VISIBLE_COLUMN; column<=FileTreeModel::LAST_VISIBLE_COLUMN; ++column)
+      if (column == FileTreeModel::COL_NAME)
+        continue;
+      if (isColumnHidden (column))
+        continue;
+
+      int minWidth = 0;
+
+      QStringList itemTexts;
+      switch (column)
         {
-          if (column == FileTreeModel::COL_NAME)
-            continue;
-          if (isColumnHidden (column))
-            continue;
-
-          QString header;
-          if (column == FileTreeModel::COL_SIZE)
-            header = QLatin1String ("999.9 KiB");
-          else
-            header = myModel->headerData (column, Qt::Horizontal).toString();
-          header += QLatin1String ("    ");
-          const int width = fontMetrics.size (0, header).width();
-          setColumnWidth (column, width);
-            left -= width;
+          case FileTreeModel::COL_SIZE:
+            for (int s = Formatter::B; s <= Formatter::TB; ++s)
+              itemTexts << QLatin1String ("999.9 ") +
+                           Formatter::unitStr (Formatter::MEM, static_cast<Formatter::Size> (s));
+            break;
+
+          case FileTreeModel::COL_PROGRESS:
+            itemTexts << QLatin1String ("  100%  ");
+            break;
+
+          case FileTreeModel::COL_WANTED:
+            minWidth = 20;
+            break;
+
+          case FileTreeModel::COL_PRIORITY:
+            itemTexts << FileTreeItem::tr ("Low") << FileTreeItem::tr ("Normal") <<
+                         FileTreeItem::tr ("High") << FileTreeItem::tr ("Mixed");
+            break;
         }
-      left -= 20; // not sure why this is necessary.  it works in different themes + font sizes though...
-      setColumnWidth(FileTreeModel::COL_NAME, std::max(left,0));
+
+      int itemWidth = 0;
+      for (const QString& itemText: itemTexts)
+        itemWidth = std::max (itemWidth, Utils::measureViewItem (this, itemText));
+
+      const QString headerText = myModel->headerData (column, Qt::Horizontal).toString ();
+      int headerWidth = Utils::measureHeaderItem (this->header (), headerText);
+
+      const int width = std::max (minWidth, std::max (itemWidth, headerWidth));
+      setColumnWidth (column, width);
+
+      left -= width;
     }
 
+  setColumnWidth (FileTreeModel::COL_NAME, std::max (left, 0));
+}
+
+void
+FileTreeView::keyPressEvent (QKeyEvent * event)
+{
+  QTreeView::keyPressEvent (event);
+
   // handle using the keyboard to toggle the
   // wanted/unwanted state or the file priority
-  else if (event->type () == QEvent::KeyPress && state () != EditingState)
+
+  if (state () == EditingState)
+    return;
+
+  if (event->key () == Qt::Key_Space)
     {
-      switch (static_cast<QKeyEvent*> (event)->key ())
-        {
-        case Qt::Key_Space:
-          for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_WANTED))
-            clicked (i);
-          break;
-
-        case Qt::Key_Enter:
-        case Qt::Key_Return:
-          for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_PRIORITY))
-            clicked (i);
-          break;
-        }
+      int column;
+
+      const Qt::KeyboardModifiers modifiers = event->modifiers ();
+      if (modifiers == Qt::NoModifier)
+        column = FileTreeModel::COL_WANTED;
+      else if (modifiers == Qt::ShiftModifier)
+        column = FileTreeModel::COL_PRIORITY;
+      else
+        return;
+
+      for (const QModelIndex& i: selectionModel ()->selectedRows (column))
+        clicked (i);
     }
-
-  return false;
 }
 
 void
index ee559a00231142230bde81c1bac98b1092e92d12..26b9f4ea6c396ab70fcc90842c8fd809ef6d601c 100644 (file)
@@ -44,8 +44,9 @@ class FileTreeView: public QTreeView
     void openRequested (const QString& path);
 
   protected:
-    // QObject
-    bool eventFilter (QObject *, QEvent *);
+    // QWidget
+    virtual void resizeEvent (QResizeEvent * event);
+    virtual void keyPressEvent (QKeyEvent * event);
 
   private:
     FileTreeModel * myModel;
index b9edc42eb8d747dbeeb95359564fc0eb4db53df7..ee23c83ad1f1c1246059d942132104db01382f4a 100644 (file)
@@ -131,6 +131,8 @@ TrackerDelegate::drawTracker (QPainter                    * painter,
                               const TrackerInfo           & inf) const
 {
   const bool isItemSelected ((option.state & QStyle::State_Selected) != 0);
+  const bool isItemEnabled ((option.state & QStyle::State_Enabled) != 0);
+  const bool isItemActive ((option.state & QStyle::State_Active) != 0);
 
   QIcon trackerIcon (inf.st.getFavicon());
 
@@ -139,6 +141,15 @@ TrackerDelegate::drawTracker (QPainter                    * painter,
 
   painter->save();
 
+  if (isItemSelected)
+    {
+      QPalette::ColorGroup cg = isItemEnabled ? QPalette::Normal : QPalette::Disabled;
+      if (cg == QPalette::Normal && !isItemActive)
+        cg = QPalette::Inactive;
+
+      painter->fillRect (option.rect, option.palette.brush (cg, QPalette::Highlight));
+    }
+
   trackerIcon.paint (painter, layout.iconRect, Qt::AlignCenter, isItemSelected ? QIcon::Selected : QIcon::Normal, QIcon::On);
 
   QAbstractTextDocumentLayout::PaintContext paintContext;
index 504fa21215bd44de509e8254f1da12d01d4800ec..c836ed4ae45b8a6d32fa06425c1896b2b9df7a28 100644 (file)
  #include <shellapi.h>
 #endif
 
+#include <QAbstractItemView>
 #include <QApplication>
 #include <QColor>
 #include <QDataStream>
 #include <QFile>
 #include <QFileDialog>
 #include <QFileInfo>
+#include <QHeaderView>
 #include <QIcon>
 #include <QInputDialog>
 #include <QObject>
@@ -204,6 +206,33 @@ Utils::removeTrailingDirSeparator (const QString& path)
   return pathInfo.fileName ().isEmpty () ? pathInfo.absolutePath () : pathInfo.absoluteFilePath ();
 }
 
+int
+Utils::measureViewItem (QAbstractItemView * view, const QString& text)
+{
+  QStyleOptionViewItemV4 option;
+  option.initFrom (view);
+  option.features = QStyleOptionViewItemV2::HasDisplay;
+  option.text = text;
+  option.textElideMode = Qt::ElideNone;
+  option.font = view->font ();
+
+  return view->style ()->sizeFromContents (QStyle::CT_ItemViewItem, &option,
+    QSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).width ();
+}
+
+int
+Utils::measureHeaderItem (QHeaderView * view, const QString& text)
+{
+  QStyleOptionHeader option;
+  option.initFrom (view);
+  option.text = text;
+  option.sortIndicator = view->isSortIndicatorShown () ? QStyleOptionHeader::SortDown :
+    QStyleOptionHeader::None;
+
+  return view->style ()->sizeFromContents (QStyle::CT_HeaderSection, &option,
+    QSize (QWIDGETSIZE_MAX, QWIDGETSIZE_MAX), view).width ();
+}
+
 QColor
 Utils::getFadedColor (const QColor& color)
 {
index 754db1101399e58827d6570548ecc56adcf47a68..09f60b8ccd9dc2697c9655e43c6ff59e69d17f8d 100644 (file)
@@ -15,7 +15,9 @@
 #include <QRect>
 #include <QString>
 
+class QAbstractItemView;
 class QColor;
+class QHeaderView;
 class QIcon;
 
 class Utils
@@ -34,6 +36,9 @@ class Utils
       rect.adjust (dx1, 0, -dx2, 0);
     }
 
+    static int measureViewItem (QAbstractItemView * view, const QString& text);
+    static int measureHeaderItem (QHeaderView * view, const QString& text);
+
     static QColor getFadedColor (const QColor& color);
 
     ///