Torrent properties dialog improvements

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.
This commit is contained in:
Mike Gelfand
2015-06-15 21:07:46 +00:00
parent b0278ca353
commit 58312e6c16
11 changed files with 197 additions and 111 deletions

View 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")));
}
/***

View File

@@ -582,9 +582,21 @@
<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>

View 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);

View 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_NAME:
value.setValue (name());
break;
case FileTreeModel::COL_SIZE:
value.setValue (sizeString() + QLatin1String (" "));
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_PROGRESS:
value.setValue (progress());
break;
case FileTreeModel::COL_WANTED:
value.setValue (isSubtreeWanted());
break;
case FileTreeModel::COL_WANTED:
value.setValue (isSubtreeWanted());
break;
case FileTreeModel::COL_PRIORITY:
value.setValue (priorityString());
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>

View 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();

View 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 ();

View File

@@ -14,8 +14,11 @@
#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))
{
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;
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;
int left = event->size ().width () - 1;
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)
{
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;
int column;
case Qt::Key_Enter:
case Qt::Key_Return:
for (const QModelIndex& i: selectionModel ()->selectedRows (FileTreeModel::COL_PRIORITY))
clicked (i);
break;
}
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

View 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;

View 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;

View File

@@ -12,12 +12,14 @@
#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)
{

View 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);
///