From 7839c4ecc01aad4c5d34a6d06b2dfc4afdc58458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Tue, 4 Feb 2014 01:40:51 +0100 Subject: Pave. --- CMakeLists.txt | 19 +- depends/groupview/.clang-format | 24 - depends/groupview/.gitignore | 2 - depends/groupview/CMakeLists.txt | 40 -- depends/groupview/Group.cpp | 169 ------ depends/groupview/Group.h | 67 --- depends/groupview/GroupView.cpp | 908 -------------------------------- depends/groupview/GroupView.h | 139 ----- depends/groupview/GroupedProxyModel.cpp | 21 - depends/groupview/GroupedProxyModel.h | 14 - depends/groupview/InstanceDelegate.cpp | 281 ---------- depends/groupview/InstanceDelegate.h | 29 - depends/groupview/main.cpp | 98 ---- depends/groupview/main.h | 54 -- gui/MainWindow.cpp | 30 +- gui/MainWindow.h | 6 +- gui/dialogs/IconPickerDialog.cpp | 2 +- gui/groupview/Group.cpp | 169 ++++++ gui/groupview/Group.h | 67 +++ gui/groupview/GroupView.cpp | 908 ++++++++++++++++++++++++++++++++ gui/groupview/GroupView.h | 139 +++++ gui/groupview/GroupedProxyModel.cpp | 26 + gui/groupview/GroupedProxyModel.h | 15 + gui/groupview/InstanceDelegate.cpp | 281 ++++++++++ gui/groupview/InstanceDelegate.h | 29 + gui/widgets/InstanceDelegate.cpp | 231 -------- gui/widgets/InstanceDelegate.h | 27 - logic/lists/InstanceList.cpp | 8 +- logic/lists/InstanceList.h | 4 +- 29 files changed, 1667 insertions(+), 2140 deletions(-) delete mode 100644 depends/groupview/.clang-format delete mode 100644 depends/groupview/.gitignore delete mode 100644 depends/groupview/CMakeLists.txt delete mode 100644 depends/groupview/Group.cpp delete mode 100644 depends/groupview/Group.h delete mode 100644 depends/groupview/GroupView.cpp delete mode 100644 depends/groupview/GroupView.h delete mode 100644 depends/groupview/GroupedProxyModel.cpp delete mode 100644 depends/groupview/GroupedProxyModel.h delete mode 100644 depends/groupview/InstanceDelegate.cpp delete mode 100644 depends/groupview/InstanceDelegate.h delete mode 100644 depends/groupview/main.cpp delete mode 100644 depends/groupview/main.h create mode 100644 gui/groupview/Group.cpp create mode 100644 gui/groupview/Group.h create mode 100644 gui/groupview/GroupView.cpp create mode 100644 gui/groupview/GroupView.h create mode 100644 gui/groupview/GroupedProxyModel.cpp create mode 100644 gui/groupview/GroupedProxyModel.h create mode 100644 gui/groupview/InstanceDelegate.cpp create mode 100644 gui/groupview/InstanceDelegate.h delete mode 100644 gui/widgets/InstanceDelegate.cpp delete mode 100644 gui/widgets/InstanceDelegate.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c34f363f..a8adfec3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,7 +186,6 @@ configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/includ ADD_DEFINITIONS(-DQUAZIP_STATIC) ADD_DEFINITIONS(-DLIBSETTINGS_STATIC) ADD_DEFINITIONS(-DLIBUTIL_STATIC) -ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC) ######## Packaging/install paths setup ######## @@ -247,10 +246,6 @@ include_directories(${LIBUTIL_INCLUDE_DIR}) add_subdirectory(depends/settings) include_directories(${LIBSETTINGS_INCLUDE_DIR}) -# Add the group view library. -add_subdirectory(depends/groupview) -include_directories(${LIBGROUPVIEW_INCLUDE_DIR}) - # Add the updater add_subdirectory(mmc_updater) @@ -317,8 +312,6 @@ gui/dialogs/UpdateDialog.cpp # GUI - widgets gui/widgets/Common.h gui/widgets/Common.cpp -gui/widgets/InstanceDelegate.h -gui/widgets/InstanceDelegate.cpp gui/widgets/ModListView.h gui/widgets/ModListView.cpp gui/widgets/VersionListView.h @@ -328,6 +321,16 @@ gui/widgets/LabeledToolButton.cpp gui/widgets/MCModInfoFrame.h gui/widgets/MCModInfoFrame.cpp +# GUI - instance group view +gui/groupview/Group.cpp +gui/groupview/Group.h +gui/groupview/GroupedProxyModel.cpp +gui/groupview/GroupedProxyModel.h +gui/groupview/GroupView.cpp +gui/groupview/GroupView.h +gui/groupview/InstanceDelegate.cpp +gui/groupview/InstanceDelegate.h + # Base classes and infrastructure logic/BaseVersion.h logic/MinecraftVersion.h @@ -584,7 +587,7 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS}) # Link TARGET_LINK_LIBRARIES(MultiMC MultiMC_common) -TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS}) +TARGET_LINK_LIBRARIES(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings ${MultiMC_LINK_ADDITIONAL_LIBS}) QT5_USE_MODULES(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES}) QT5_USE_MODULES(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES}) diff --git a/depends/groupview/.clang-format b/depends/groupview/.clang-format deleted file mode 100644 index 167a8fa7..00000000 --- a/depends/groupview/.clang-format +++ /dev/null @@ -1,24 +0,0 @@ -UseTab: true -IndentWidth: 4 -TabWidth: 4 -ConstructorInitializerIndentWidth: 4 -AccessModifierOffset: -4 -IndentCaseLabels: false -IndentFunctionDeclarationAfterType: false -NamespaceIndentation: None - -BreakBeforeBraces: Allman -AllowShortIfStatementsOnASingleLine: false -ColumnLimit: 96 -MaxEmptyLinesToKeep: 1 - -Standard: Cpp11 -Cpp11BracedListStyle: true - -SpacesInParentheses: false -SpaceInEmptyParentheses: false -SpacesInCStyleCastParentheses: false -SpaceAfterControlStatementKeyword: true - -AlignTrailingComments: true -SpacesBeforeTrailingComments: 1 diff --git a/depends/groupview/.gitignore b/depends/groupview/.gitignore deleted file mode 100644 index a5d18fa3..00000000 --- a/depends/groupview/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build/ -*.user* diff --git a/depends/groupview/CMakeLists.txt b/depends/groupview/CMakeLists.txt deleted file mode 100644 index e2a85950..00000000 --- a/depends/groupview/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 2.8.9) - -project(GroupView) - -set(CMAKE_AUTOMOC ON) - -IF(APPLE) - message(STATUS "Using APPLE CMAKE_CXX_FLAGS") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -ELSEIF(UNIX) - # assume GCC, add C++0x/C++11 stuff - MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") -ELSEIF(MINGW) - MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall") -ENDIF() - -find_package(Qt5Core REQUIRED) -find_package(Qt5Gui REQUIRED) -find_package(Qt5Widgets REQUIRED) - -include_directories(${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS}) - -set(SOURCES - main.cpp - main.h - - GroupView.h - GroupView.cpp - Group.h - Group.cpp - GroupedProxyModel.h - GroupedProxyModel.cpp - InstanceDelegate.h - InstanceDelegate.cpp -) - -add_executable(GroupView ${SOURCES}) -qt5_use_modules(GroupView Core Gui Widgets) diff --git a/depends/groupview/Group.cpp b/depends/groupview/Group.cpp deleted file mode 100644 index f216cc6e..00000000 --- a/depends/groupview/Group.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include "Group.h" - -#include -#include -#include - -#include "GroupView.h" - -Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) -{ -} - -Group::Group(const Group *other) - : view(other->view), text(other->text), collapsed(other->collapsed) -{ -} - -void Group::update() -{ - firstItemIndex = firstItem().row(); - - rowHeights = QVector(numRows()); - for (int i = 0; i < numRows(); ++i) - { - rowHeights[i] = view->categoryRowHeight( - view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0)); - } -} - -Group::HitResults Group::hitScan(const QPoint &pos) const -{ - Group::HitResults results = Group::NoHit; - int y_start = verticalPosition(); - int body_start = y_start + headerHeight(); - int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? - int y = pos.y(); - // int x = pos.x(); - if(y < y_start) - { - results = Group::NoHit; - } - else if(y < body_start) - { - results = Group::HeaderHit; - int collapseSize = headerHeight() - 4; - - // the icon - QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); - if(iconRect.contains(pos)) - { - results |= Group::CheckboxHit; - } - } - else if (y < body_end) - { - results |= Group::BodyHit; - } - return results; -} - -void Group::drawHeader(QPainter *painter, const int y) -{ - painter->save(); - - int height = headerHeight() - 4; - int collapseSize = height; - - // the icon - QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y, collapseSize, collapseSize); - painter->setPen(QPen(Qt::black, 1)); - painter->drawRect(iconRect); - static const int margin = 2; - QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); - int midX = iconSubrect.center().x(); - int midY = iconSubrect.center().y(); - if (collapsed) - { - painter->drawLine(midX, iconSubrect.top(), midX, iconSubrect.bottom()); - } - painter->drawLine(iconSubrect.left(), midY, iconSubrect.right(), midY); - - // the text - int textWidth = painter->fontMetrics().width(text); - QRect textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); - painter->setBrush(view->viewOptions().palette.text()); - view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, - view->viewport()->palette(), true, text); - - // the line - painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, - view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); - - painter->restore(); -} - -int Group::totalHeight() const -{ - return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'? -} - -int Group::headerHeight() const -{ - return view->viewport()->fontMetrics().height() + 4; -} - -int Group::contentHeight() const -{ - if (collapsed) - { - return 0; - } - int result = 0; - for (int i = 0; i < rowHeights.size(); ++i) - { - result += rowHeights[i]; - } - return result; -} - -int Group::numRows() const -{ - return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow())); -} - -int Group::verticalPosition() const -{ - int res = 0; - const QList cats = view->m_groups; - for (int i = 0; i < cats.size(); ++i) - { - if (cats.at(i) == this) - { - break; - } - res += cats.at(i)->totalHeight() + view->m_categoryMargin; - } - return res; -} - -QList Group::items() const -{ - QList indices; - for (int i = 0; i < view->model()->rowCount(); ++i) - { - const QModelIndex index = view->model()->index(i, 0); - if (index.data(GroupViewRoles::GroupRole).toString() == text) - { - indices.append(index); - } - } - return indices; -} - -int Group::numItems() const -{ - return items().size(); -} - -QModelIndex Group::firstItem() const -{ - QList indices = items(); - return indices.isEmpty() ? QModelIndex() : indices.first(); -} - -QModelIndex Group::lastItem() const -{ - QList indices = items(); - return indices.isEmpty() ? QModelIndex() : indices.last(); -} diff --git a/depends/groupview/Group.h b/depends/groupview/Group.h deleted file mode 100644 index 455ee1a8..00000000 --- a/depends/groupview/Group.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include - -class GroupView; -class QPainter; -class QModelIndex; - -struct Group -{ -/* constructors */ - Group(const QString &text, GroupView *view); - Group(const Group *other); - -/* data */ - GroupView *view; - QString text; - bool collapsed; - QVector rowHeights; - int firstItemIndex; - -/* logic */ - /// do stuff. and things. TODO: redo. - void update(); - - /// draw the header at y-position. - void drawHeader(QPainter *painter, const int y); - - /// height of the group, in total. includes a small bit of padding. - int totalHeight() const; - - /// height of the group header, in pixels - int headerHeight() const; - - /// height of the group content, in pixels - int contentHeight() const; - - /// the number of visual rows this group has - int numRows() const; - - /// the height at which this group starts, in pixels - int verticalPosition() const; - - enum HitResult - { - NoHit = 0x0, - TextHit = 0x1, - CheckboxHit = 0x2, - HeaderHit = 0x4, - BodyHit = 0x8 - }; - Q_DECLARE_FLAGS(HitResults, HitResult) - - /// shoot! BANG! what did we hit? - HitResults hitScan (const QPoint &pos) const; - - /// super derpy thing. - QList items() const; - /// I don't even - int numItems() const; - QModelIndex firstItem() const; - QModelIndex lastItem() const; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults) diff --git a/depends/groupview/GroupView.cpp b/depends/groupview/GroupView.cpp deleted file mode 100644 index 89e3e223..00000000 --- a/depends/groupview/GroupView.cpp +++ /dev/null @@ -1,908 +0,0 @@ -#include "GroupView.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Group.h" - -template bool listsIntersect(const QList &l1, const QList t2) -{ - for (auto &item : l1) - { - if (t2.contains(item)) - { - return true; - } - } - return false; -} - -GroupView::GroupView(QWidget *parent) - : QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), - m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) -{ - // setViewMode(IconMode); - // setMovement(Snap); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - // setWordWrap(true); - // setDragDropMode(QListView::InternalMove); - setAcceptDrops(true); - m_spacing = 5; -} - -GroupView::~GroupView() -{ - qDeleteAll(m_groups); - m_groups.clear(); -} - -void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVector &roles) -{ - if (roles.contains(GroupViewRoles::GroupRole) || roles.contains(Qt::DisplayRole)) - { - updateGeometries(); - } - viewport()->update(); -} -void GroupView::rowsInserted(const QModelIndex &parent, int start, int end) -{ - updateGeometries(); - viewport()->update(); -} - -void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) -{ - updateGeometries(); - viewport()->update(); -} - -void GroupView::updateGeometries() -{ - int previousScroll = verticalScrollBar()->value(); - - QMap cats; - - for (int i = 0; i < model()->rowCount(); ++i) - { - const QString groupName = - model()->index(i, 0).data(GroupViewRoles::GroupRole).toString(); - if (!cats.contains(groupName)) - { - Group *old = this->category(groupName); - if (old) - { - cats.insert(groupName, new Group(old)); - } - else - { - cats.insert(groupName, new Group(groupName, this)); - } - } - } - - /*if (m_editedCategory) - { - m_editedCategory = cats[m_editedCategory->text]; - }*/ - - qDeleteAll(m_groups); - m_groups = cats.values(); - - for (auto cat : m_groups) - { - cat->update(); - } - - if (m_groups.isEmpty()) - { - verticalScrollBar()->setRange(0, 0); - } - else - { - int totalHeight = 0; - for (auto category : m_groups) - { - totalHeight += category->totalHeight() + m_categoryMargin; - } - // remove the last margin (we don't want it) - totalHeight -= m_categoryMargin; - totalHeight += m_bottomMargin; - verticalScrollBar()->setRange(0, totalHeight - height()); - } - - verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum())); - - viewport()->update(); -} - -bool GroupView::isIndexHidden(const QModelIndex &index) const -{ - Group *cat = category(index); - if (cat) - { - return cat->collapsed; - } - else - { - return false; - } -} - -Group *GroupView::category(const QModelIndex &index) const -{ - return category(index.data(GroupViewRoles::GroupRole).toString()); -} - -Group *GroupView::category(const QString &cat) const -{ - for (auto group : m_groups) - { - if (group->text == cat) - { - return group; - } - } - return nullptr; -} - -Group *GroupView::categoryAt(const QPoint &pos) const -{ - for (auto group : m_groups) - { - if(group->hitScan(pos) & Group::CheckboxHit) - { - return group; - } - } - return nullptr; -} - -int GroupView::itemsPerRow() const -{ - return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing)); -} - -int GroupView::contentWidth() const -{ - return width() - m_leftMargin - m_rightMargin; -} - -int GroupView::itemWidth() const -{ - return itemDelegate() - ->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0)) - .width(); -} - -int GroupView::categoryRowHeight(const QModelIndex &index) const -{ - QModelIndexList indices; - int internalRow = categoryInternalPosition(index).second; - for (auto &i : category(index)->items()) - { - if (categoryInternalPosition(i).second == internalRow) - { - indices.append(i); - } - } - - int largestHeight = 0; - for (auto &i : indices) - { - largestHeight = - qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); - } - return largestHeight; -} - -QPair GroupView::categoryInternalPosition(const QModelIndex &index) const -{ - QList indices = category(index)->items(); - int x = 0; - int y = 0; - const int perRow = itemsPerRow(); - for (int i = 0; i < indices.size(); ++i) - { - if (indices.at(i) == index) - { - break; - } - ++x; - if (x == perRow) - { - x = 0; - ++y; - } - } - return qMakePair(x, y); -} - -int GroupView::categoryInternalRowTop(const QModelIndex &index) const -{ - Group *cat = category(index); - int categoryInternalRow = categoryInternalPosition(index).second; - int result = 0; - for (int i = 0; i < categoryInternalRow; ++i) - { - result += cat->rowHeights.at(i); - } - return result; -} - -int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const -{ - for (auto &i : category->items()) - { - QPair pos = categoryInternalPosition(i); - if (pos.second == internalRow) - { - return categoryRowHeight(i); - } - } - return -1; -} - -void GroupView::mousePressEvent(QMouseEvent *event) -{ - // endCategoryEditor(); - - QPoint pos = event->pos() + offset(); - QPersistentModelIndex index = indexAt(pos); - - m_pressedIndex = index; - m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); - QItemSelectionModel::SelectionFlags selection_flags = selectionCommand(index, event); - m_pressedPosition = pos; - - m_pressedCategory = categoryAt(m_pressedPosition); - if (m_pressedCategory) - { - setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); - event->accept(); - return; - } - - if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) - { - // we disable scrollTo for mouse press so the item doesn't change position - // when the user is interacting with it (ie. clicking on it) - bool autoScroll = hasAutoScroll(); - setAutoScroll(false); - selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); - setAutoScroll(autoScroll); - QRect rect(m_pressedPosition, pos); - setSelection(rect, QItemSelectionModel::ClearAndSelect); - - // signal handlers may change the model - emit pressed(index); - } - else - { - // Forces a finalize() even if mouse is pressed, but not on a item - selectionModel()->select(QModelIndex(), QItemSelectionModel::Select); - } -} - -void GroupView::mouseMoveEvent(QMouseEvent *event) -{ - QPoint topLeft; - QPoint pos = event->pos() + offset(); - - if (state() == ExpandingState || state() == CollapsingState) - { - return; - } - - if (state() == DraggingState) - { - topLeft = m_pressedPosition - offset(); - if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) - { - m_pressedIndex = QModelIndex(); - startDrag(model()->supportedDragActions()); - setState(NoState); - stopAutoScroll(); - } - return; - } - - if (selectionMode() != SingleSelection) - { - topLeft = m_pressedPosition - offset(); - } - else - { - topLeft = pos; - } - - if (m_pressedIndex.isValid() && (state() != DragSelectingState) && - (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty()) - { - setState(DraggingState); - return; - } - - if ((event->buttons() & Qt::LeftButton) && selectionModel()) - { - setState(DragSelectingState); - - setSelection(QRect(pos, pos), QItemSelectionModel::ClearAndSelect); - QModelIndex index = indexAt(pos); - - // set at the end because it might scroll the view - if (index.isValid() && (index != selectionModel()->currentIndex()) && - (index.flags() & Qt::ItemIsEnabled)) - { - selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); - } - } -} - -void GroupView::mouseReleaseEvent(QMouseEvent *event) -{ - QPoint pos = event->pos() + offset(); - QPersistentModelIndex index = indexAt(pos); - - bool click = (index == m_pressedIndex && index.isValid()) || - (m_pressedCategory && m_pressedCategory == categoryAt(pos)); - - if (click && m_pressedCategory) - { - if (state() == ExpandingState) - { - m_pressedCategory->collapsed = false; - updateGeometries(); - viewport()->update(); - event->accept(); - return; - } - else if (state() == CollapsingState) - { - m_pressedCategory->collapsed = true; - updateGeometries(); - viewport()->update(); - event->accept(); - return; - } - } - - m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; - - setState(NoState); - - if (click) - { - if (event->button() == Qt::LeftButton) - { - emit clicked(index); - } - QStyleOptionViewItem option = viewOptions(); - if (m_pressedAlreadySelected) - { - option.state |= QStyle::State_Selected; - } - if ((model()->flags(index) & Qt::ItemIsEnabled) && - style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) - { - emit activated(index); - } - } -} - -void GroupView::mouseDoubleClickEvent(QMouseEvent *event) -{ - QModelIndex index = indexAt(event->pos()); - if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index)) - { - QMouseEvent me(QEvent::MouseButtonPress, event->localPos(), event->windowPos(), - event->screenPos(), event->button(), event->buttons(), - event->modifiers()); - mousePressEvent(&me); - return; - } - // signal handlers may change the model - QPersistentModelIndex persistent = index; - emit doubleClicked(persistent); -} - -void GroupView::paintEvent(QPaintEvent *event) -{ - QPainter painter(this->viewport()); - - int y = -verticalOffset(); - for (int i = 0; i < m_groups.size(); ++i) - { - Group *category = m_groups.at(i); - category->drawHeader(&painter, y); - y += category->totalHeight() + m_categoryMargin; - } - - for (int i = 0; i < model()->rowCount(); ++i) - { - const QModelIndex index = model()->index(i, 0); - if (isIndexHidden(index)) - { - continue; - } - Qt::ItemFlags flags = index.flags(); - QStyleOptionViewItemV4 option(viewOptions()); - option.rect = visualRect(index); - option.widget = this; - option.features |= - QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway? - if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) - { - option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected - : QStyle::State_None; - } - else - { - option.state &= ~QStyle::State_Selected; - } - option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None; - if (!(flags & Qt::ItemIsEnabled)) - { - option.state &= ~QStyle::State_Enabled; - } - itemDelegate()->paint(&painter, option, index); - } - - /* - * Drop indicators for manual reordering... - */ -#if 0 - if (!m_lastDragPosition.isNull()) - { - QPair pair = rowDropPos(m_lastDragPosition); - Group *category = pair.first; - int row = pair.second; - if (category) - { - int internalRow = row - category->firstItemIndex; - QLine line; - if (internalRow >= category->numItems()) - { - QRect toTheRightOfRect = visualRect(category->lastItem()); - line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight()); - } - else - { - QRect toTheLeftOfRect = visualRect(model()->index(row, 0)); - line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft()); - } - painter.save(); - painter.setPen(QPen(Qt::black, 3)); - painter.drawLine(line); - painter.restore(); - } - } -#endif -} - -void GroupView::resizeEvent(QResizeEvent *event) -{ - // QListView::resizeEvent(event); - - // if (m_categoryEditor) - // { - // m_categoryEditor->resize(qMax(contentWidth() / 2, - // m_editedCategory->textRect.width()), - // m_categoryEditor->height()); - // } - - updateGeometries(); -} - -void GroupView::dragEnterEvent(QDragEnterEvent *event) -{ - if (!isDragEventAccepted(event)) - { - return; - } - m_lastDragPosition = event->pos() + offset(); - viewport()->update(); - event->accept(); -} - -void GroupView::dragMoveEvent(QDragMoveEvent *event) -{ - if (!isDragEventAccepted(event)) - { - return; - } - m_lastDragPosition = event->pos() + offset(); - viewport()->update(); - event->accept(); -} - -void GroupView::dragLeaveEvent(QDragLeaveEvent *event) -{ - m_lastDragPosition = QPoint(); - viewport()->update(); -} - -void GroupView::dropEvent(QDropEvent *event) -{ - m_lastDragPosition = QPoint(); - - stopAutoScroll(); - setState(NoState); - - if (event->source() != this || !(event->possibleActions() & Qt::MoveAction)) - { - return; - } - - QPair dropPos = rowDropPos(event->pos() + offset()); - const Group *category = dropPos.first; - const int row = dropPos.second; - - if (row == -1) - { - viewport()->update(); - return; - } - - const QString categoryText = category->text; - if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) - { - model()->setData(model()->index(row, 0), categoryText, - GroupViewRoles::GroupRole); - event->setDropAction(Qt::MoveAction); - event->accept(); - } - updateGeometries(); - viewport()->update(); -} - -void GroupView::startDrag(Qt::DropActions supportedActions) -{ - QModelIndexList indexes = selectionModel()->selectedIndexes(); - if (indexes.count() > 0) - { - QMimeData *data = model()->mimeData(indexes); - if (!data) - { - return; - } - QRect rect; - QPixmap pixmap = renderToPixmap(indexes, &rect); - //rect.translate(offset()); - // rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); - QDrag *drag = new QDrag(this); - drag->setPixmap(pixmap); - drag->setMimeData(data); - drag->setHotSpot(m_pressedPosition - rect.topLeft()); - Qt::DropAction defaultDropAction = Qt::IgnoreAction; - if (this->defaultDropAction() != Qt::IgnoreAction && - (supportedActions & this->defaultDropAction())) - { - defaultDropAction = this->defaultDropAction(); - } - if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) - { - const QItemSelection selection = selectionModel()->selection(); - - for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) - { - QModelIndex parent = (*it).parent(); - if ((*it).left() != 0) - { - continue; - } - if ((*it).right() != (model()->columnCount(parent) - 1)) - { - continue; - } - int count = (*it).bottom() - (*it).top() + 1; - model()->removeRows((*it).top(), count, parent); - } - } - } -} - -QRect GroupView::visualRect(const QModelIndex &index) const -{ - return geometryRect(index).translated(-offset()); -} - -QRect GroupView::geometryRect(const QModelIndex &index) const -{ - if (!index.isValid() || isIndexHidden(index) || index.column() > 0) - { - return QRect(); - } - - const Group *cat = category(index); - QPair pos = categoryInternalPosition(index); - int x = pos.first; - // int y = pos.second; - - QRect out; - out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); - out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); - out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); - - return out; -} - -/* -void CategorizedView::startCategoryEditor(Category *category) -{ - if (m_categoryEditor != 0) - { - return; - } - m_editedCategory = category; - m_categoryEditor = new QLineEdit(m_editedCategory->text, this); - QRect rect = m_editedCategory->textRect; - rect.setWidth(qMax(contentWidth() / 2, rect.width())); - m_categoryEditor->setGeometry(rect); - m_categoryEditor->show(); - m_categoryEditor->setFocus(); - connect(m_categoryEditor, &QLineEdit::returnPressed, this, -&CategorizedView::endCategoryEditor); -} - -void CategorizedView::endCategoryEditor() -{ - if (m_categoryEditor == 0) - { - return; - } - m_editedCategory->text = m_categoryEditor->text(); - m_updatesDisabled = true; - foreach (const QModelIndex &index, itemsForCategory(m_editedCategory)) - { - const_cast(index.model())->setData(index, -m_categoryEditor->text(), CategoryRole); - } - m_updatesDisabled = false; - delete m_categoryEditor; - m_categoryEditor = 0; - m_editedCategory = 0; - updateGeometries(); -} -*/ - -QModelIndex GroupView::indexAt(const QPoint &point) const -{ - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - if (geometryRect(index).contains(point)) - { - return index; - } - } - return QModelIndex(); -} - -void GroupView::setSelection(const QRect &rect, - const QItemSelectionModel::SelectionFlags commands) -{ - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - QRect itemRect = geometryRect(index); - if (itemRect.intersects(rect)) - { - selectionModel()->select(index, commands); - update(itemRect.translated(-offset())); - } - } - -} - -QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const -{ - Q_ASSERT(r); - auto paintPairs = draggablePaintPairs(indices, r); - if (paintPairs.isEmpty()) - { - return QPixmap(); - } - QPixmap pixmap(r->size()); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - QStyleOptionViewItem option = viewOptions(); - option.state |= QStyle::State_Selected; - for (int j = 0; j < paintPairs.count(); ++j) - { - option.rect = paintPairs.at(j).first.translated(-r->topLeft()); - const QModelIndex ¤t = paintPairs.at(j).second; - itemDelegate()->paint(&painter, option, current); - } - return pixmap; -} - -QList> GroupView::draggablePaintPairs(const QModelIndexList &indices, - QRect *r) const -{ - Q_ASSERT(r); - QRect &rect = *r; - QList> ret; - for (int i = 0; i < indices.count(); ++i) - { - const QModelIndex &index = indices.at(i); - const QRect current = geometryRect(index); - ret += qMakePair(current, index); - rect |= current; - } - return ret; -} - -bool GroupView::isDragEventAccepted(QDropEvent *event) -{ - if (event->source() != this) - { - return false; - } - if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes())) - { - return false; - } - if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), - rowDropPos(event->pos()).second, 0, QModelIndex())) - { - return false; - } - return true; -} - -QPair GroupView::rowDropPos(const QPoint &pos) -{ - // check that we aren't on a category header and calculate which category we're in - Group *category = 0; - { - int y = 0; - for (auto cat : m_groups) - { - if (pos.y() > y && pos.y() < (y + cat->headerHeight())) - { - return qMakePair(nullptr, -1); - } - y += cat->totalHeight() + m_categoryMargin; - if (pos.y() < y) - { - category = cat; - break; - } - } - if (category == 0) - { - return qMakePair(nullptr, -1); - } - } - - QList indices = category->items(); - - // calculate the internal column - int internalColumn = -1; - { - const int itemWidth = this->itemWidth(); - if (pos.x() >= (itemWidth * itemsPerRow())) - { - internalColumn = itemsPerRow(); - } - else - { - for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c) - { - if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2)) - { - internalColumn = c; - break; - } - } - } - if (internalColumn == -1) - { - return qMakePair(nullptr, -1); - } - } - - // calculate the internal row - int internalRow = -1; - { - // FIXME rework the drag and drop code - const int top = category->verticalPosition(); - for (int r = 0, h = top; r < category->numRows(); - h += itemHeightForCategoryRow(category, r), ++r) - { - if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r))) - { - internalRow = r; - break; - } - } - if (internalRow == -1) - { - return qMakePair(nullptr, -1); - } - // this happens if we're in the margin between a one category and another - // categories header - if (internalRow > (indices.size() / itemsPerRow())) - { - return qMakePair(nullptr, -1); - } - } - - // flaten the internalColumn/internalRow to one row - int categoryRow = internalRow * itemsPerRow() + internalColumn; - - // this is used if we're past the last item - if (categoryRow >= indices.size()) - { - return qMakePair(category, indices.last().row() + 1); - } - - return qMakePair(category, indices.at(categoryRow).row()); -} - -QPoint GroupView::offset() const -{ - return QPoint(horizontalOffset(), verticalOffset()); -} - -QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) const -{ - QRegion region; - for (auto &range : selection) - { - int start_row = range.top(); - int end_row = range.bottom(); - for (int row = start_row; row <= end_row; ++row) - { - int start_column = range.left(); - int end_column = range.right(); - for (int column = start_column; column <= end_column; ++column) - { - QModelIndex index = model()->index(row, column, rootIndex()); - region += visualRect(index); // OK - } - } - } - return region; -} -QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) -{ - auto current = currentIndex(); - if(!current.isValid()) - { - qDebug() << "model row: invalid"; - return current; - } - qDebug() << "model row: " << current.row(); - auto cat = category(current); - int i = m_groups.indexOf(cat); - if(i >= 0) - { - // this is a pile of something foul - auto real_group = m_groups[i]; - int beginning_row = 0; - for(auto group: m_groups) - { - if(group == real_group) - break; - beginning_row += group->numRows(); - } - qDebug() << "category: " << real_group->text; - QPair pos = categoryInternalPosition(current); - int row = beginning_row + pos.second; - qDebug() << "row: " << row; - qDebug() << "column: " << pos.first; - } - return current; -} diff --git a/depends/groupview/GroupView.h b/depends/groupview/GroupView.h deleted file mode 100644 index 329a3503..00000000 --- a/depends/groupview/GroupView.h +++ /dev/null @@ -1,139 +0,0 @@ -#pragma once - -#include -#include -#include - -struct GroupViewRoles -{ - enum - { - GroupRole = Qt::UserRole, - ProgressValueRole, - ProgressMaximumRole - }; -}; - -struct Group; - -class GroupView : public QAbstractItemView -{ - Q_OBJECT - -public: - GroupView(QWidget *parent = 0); - ~GroupView(); - - QRect geometryRect(const QModelIndex &index) const; - virtual QRect visualRect(const QModelIndex &index) const override; - QModelIndex indexAt(const QPoint &point) const; - void setSelection(const QRect &rect, - const QItemSelectionModel::SelectionFlags commands) override; - - virtual int horizontalOffset() const override - { - return horizontalScrollBar()->value(); - } - - virtual int verticalOffset() const override - { - return verticalScrollBar()->value(); - } - - virtual void scrollContentsBy(int dx, int dy) override - { - scrollDirtyRegion(dx, dy); - viewport()->scroll(dx, dy); - } - - /* - * TODO! - */ - virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override - { - return; - } - - virtual QModelIndex moveCursor(CursorAction cursorAction, - Qt::KeyboardModifiers modifiers) override; - - virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; - -protected -slots: - virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVector &roles) override; - virtual void rowsInserted(const QModelIndex &parent, int start, int end) override; - virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override; - virtual void updateGeometries() override; - -protected: - virtual bool isIndexHidden(const QModelIndex &index) const override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - - void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; - - void startDrag(Qt::DropActions supportedActions) override; - -private: - friend struct Group; - - QList m_groups; - - int m_leftMargin; - int m_rightMargin; - int m_bottomMargin; - int m_categoryMargin; - - // bool m_updatesDisabled; - - Group *category(const QModelIndex &index) const; - Group *category(const QString &cat) const; - Group *categoryAt(const QPoint &pos) const; - - int itemsPerRow() const; - int contentWidth() const; - -private: - int itemWidth() const; - int categoryRowHeight(const QModelIndex &index) const; - - /*QLineEdit *m_categoryEditor; - Category *m_editedCategory; - void startCategoryEditor(Category *category); - -private slots: - void endCategoryEditor();*/ - -private: /* variables */ - QPoint m_pressedPosition; - QPersistentModelIndex m_pressedIndex; - bool m_pressedAlreadySelected; - Group *m_pressedCategory; - QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; - QPoint m_lastDragPosition; - int m_spacing = 5; - -private: /* methods */ - QPair categoryInternalPosition(const QModelIndex &index) const; - int categoryInternalRowTop(const QModelIndex &index) const; - int itemHeightForCategoryRow(const Group *category, const int internalRow) const; - - QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; - QList> draggablePaintPairs(const QModelIndexList &indices, - QRect *r) const; - - bool isDragEventAccepted(QDropEvent *event); - - QPair rowDropPos(const QPoint &pos); - - QPoint offset() const; -}; diff --git a/depends/groupview/GroupedProxyModel.cpp b/depends/groupview/GroupedProxyModel.cpp deleted file mode 100644 index 57d7ff5c..00000000 --- a/depends/groupview/GroupedProxyModel.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "GroupedProxyModel.h" - -#include "GroupView.h" - -GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent) -{ -} - -bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString(); - const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString(); - if (leftCategory == rightCategory) - { - return left.row() < right.row(); - } - else - { - return leftCategory < rightCategory; - } -} diff --git a/depends/groupview/GroupedProxyModel.h b/depends/groupview/GroupedProxyModel.h deleted file mode 100644 index cae87ecd..00000000 --- a/depends/groupview/GroupedProxyModel.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -class GroupedProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - GroupedProxyModel(QObject *parent = 0); - -protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; -}; diff --git a/depends/groupview/InstanceDelegate.cpp b/depends/groupview/InstanceDelegate.cpp deleted file mode 100644 index 8527e2bc..00000000 --- a/depends/groupview/InstanceDelegate.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InstanceDelegate.h" -#include -#include -#include -#include -#include - -#include "GroupView.h" - -// Origin: Qt -static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, - qreal &widthUsed) -{ - height = 0; - widthUsed = 0; - textLayout.beginLayout(); - QString str = textLayout.text(); - while (true) - { - QTextLine line = textLayout.createLine(); - if (!line.isValid()) - break; - if (line.textLength() == 0) - break; - line.setLineWidth(lineWidth); - line.setPosition(QPointF(0, height)); - height += line.height(); - widthUsed = qMax(widthUsed, line.naturalTextWidth()); - } - textLayout.endLayout(); -} - -#define QFIXED_MAX (INT_MAX / 256) - -ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) -{ -} - -void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, - const QRect &rect) -{ - if ((option.state & QStyle::State_Selected)) - painter->fillRect(rect, option.palette.brush(QPalette::Highlight)); - else - { - QColor backgroundColor = option.palette.color(QPalette::Background); - backgroundColor.setAlpha(160); - painter->fillRect(rect, QBrush(backgroundColor)); - } -} - -void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect) -{ - if (!(option.state & QStyle::State_HasFocus)) - return; - QStyleOptionFocusRect opt; - opt.direction = option.direction; - opt.fontMetrics = option.fontMetrics; - opt.palette = option.palette; - opt.rect = rect; - // opt.state = option.state | QStyle::State_KeyboardFocusChange | - // QStyle::State_Item; - auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base; - opt.backgroundColor = option.palette.color(col); - // Apparently some widget styles expect this hint to not be set - painter->setRenderHint(QPainter::Antialiasing, false); - - QStyle *style = option.widget ? option.widget->style() : QApplication::style(); - - style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); - - painter->setRenderHint(QPainter::Antialiasing); -} - -// TODO this can be made a lot prettier -void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option, - const int value, const int maximum) -{ - if (maximum == 0 || value == maximum) - { - return; - } - - painter->save(); - - qreal percent = (qreal)value / (qreal)maximum; - QColor color = option.palette.color(QPalette::Dark); - color.setAlphaF(0.70f); - painter->setBrush(color); - painter->setPen(QPen(QBrush(), 0)); - painter->drawPie(option.rect, 90 * 16, -percent * 360 * 16); - - painter->restore(); -} - -static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option) -{ - QStyle *style = option->widget ? option->widget->style() : QApplication::style(); - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(option->font); - textLayout.setText(option->text); - const int textMargin = - style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1; - QRect bounds(0, 0, 100 - 2 * textMargin, 600); - qreal height = 0, widthUsed = 0; - viewItemTextLayout(textLayout, bounds.width(), height, widthUsed); - const QSize size(qCeil(widthUsed), qCeil(height)); - return QSize(size.width() + 2 * textMargin, size.height()); -} - -void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - painter->save(); - painter->setClipRect(opt.rect); - - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - - // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); - const int iconSize = 48; - QRect iconbox = opt.rect; - const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; - QRect textRect = opt.rect; - QRect textHighlightRect = textRect; - // clip the decoration on top, remove width padding - textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0); - - textHighlightRect.adjust(0, iconSize + 5, 0, 0); - - // draw background - { - // FIXME: unused - // QSize textSize = viewItemTextSize ( &opt ); - QPalette::ColorGroup cg; - QStyleOptionViewItemV4 opt2(opt); - - if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) - { - if (!(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - else - cg = QPalette::Normal; - } - else - { - cg = QPalette::Disabled; - } - opt2.palette.setCurrentColorGroup(cg); - - // fill in background, if any - if (opt.backgroundBrush.style() != Qt::NoBrush) - { - QPointF oldBO = painter->brushOrigin(); - painter->setBrushOrigin(opt.rect.topLeft()); - painter->fillRect(opt.rect, opt.backgroundBrush); - painter->setBrushOrigin(oldBO); - } - - if (opt.showDecorationSelected) - { - drawSelectionRect(painter, opt2, opt.rect); - drawFocusRect(painter, opt2, opt.rect); - // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); - } - else - { - - // if ( opt.state & QStyle::State_Selected ) - { - // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, - // opt.widget ); - // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, - // QPalette::Highlight ) ); - drawSelectionRect(painter, opt2, textHighlightRect); - drawFocusRect(painter, opt2, textHighlightRect); - } - } - } - - // draw the icon - { - QIcon::Mode mode = QIcon::Normal; - if (!(opt.state & QStyle::State_Enabled)) - mode = QIcon::Disabled; - else if (opt.state & QStyle::State_Selected) - mode = QIcon::Selected; - QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; - - iconbox.setHeight(iconSize); - opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); - } - // set the text colors - QPalette::ColorGroup cg = - opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - if (opt.state & QStyle::State_Selected) - { - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - } - else - { - painter->setPen(opt.palette.color(cg, QPalette::Text)); - } - - // draw the text - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - textOption.setTextDirection(opt.direction); - textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment)); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(opt.font); - textLayout.setText(opt.text); - - qreal width, height; - viewItemTextLayout(textLayout, textRect.width(), height, width); - - const int lineCount = textLayout.lineCount(); - - const QRect layoutRect = QStyle::alignedRect( - opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); - const QPointF position = layoutRect.topLeft(); - for (int i = 0; i < lineCount; ++i) - { - const QTextLine line = textLayout.lineAt(i); - line.draw(painter, position); - } - - drawProgressOverlay(painter, opt, - index.data(GroupViewRoles::ProgressValueRole).toInt(), - index.data(GroupViewRoles::ProgressMaximumRole).toInt()); - - painter->restore(); -} - -QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - const int textMargin = - style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; - int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables - QSize szz = viewItemTextSize(&opt); - height += szz.height(); - // FIXME: maybe the icon items could scale and keep proportions? - QSize sz(100, height); - return sz; -} diff --git a/depends/groupview/InstanceDelegate.h b/depends/groupview/InstanceDelegate.h deleted file mode 100644 index de2f429b..00000000 --- a/depends/groupview/InstanceDelegate.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -class ListViewDelegate : public QStyledItemDelegate -{ -public: - explicit ListViewDelegate(QObject *parent = 0); - -protected: - void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; -}; diff --git a/depends/groupview/main.cpp b/depends/groupview/main.cpp deleted file mode 100644 index df14e3bd..00000000 --- a/depends/groupview/main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "main.h" - -#include -#include -#include -#include - -#include "GroupView.h" -#include "GroupedProxyModel.h" -#include "InstanceDelegate.h" - -Progresser *progresser; - -QPixmap icon(const Qt::GlobalColor color) -{ - QPixmap p = QPixmap(32, 32); - p.fill(QColor(color)); - return p; -} -QPixmap icon(const int number) -{ - QPixmap p = icon(Qt::white); - QPainter painter(&p); - QFont font = painter.font(); - font.setBold(true); - font.setPixelSize(28); - painter.setFont(font); - painter.drawText(QRect(QPoint(0, 0), p.size()), Qt::AlignVCenter | Qt::AlignHCenter, - QString::number(number)); - painter.end(); - return p; -} -QStandardItem *createItem(const Qt::GlobalColor color, const QString &text, - const QString &category) -{ - QStandardItem *item = new QStandardItem; - item->setText(text); - item->setData(icon(color), Qt::DecorationRole); - item->setData(category, GroupViewRoles::GroupRole); - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - // progresser->addTrackedIndex(item); - return item; -} -QStandardItem *createItem(const int index, const QString &category) -{ - QStandardItem *item = new QStandardItem; - item->setText(QString("Item #%1").arg(index)); - item->setData(icon(index), Qt::DecorationRole); - item->setData(category, GroupViewRoles::GroupRole); - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - // progresser->addTrackedIndex(item); - return item; -} - -int main(int argc, char *argv[]) -{ - QApplication a(argc, argv); - - qsrand(QTime::currentTime().msec()); - - progresser = new Progresser(); - - QStandardItemModel model; - model.setRowCount(10); - model.setColumnCount(1); - - model.setItem( - 0, createItem(Qt::red, - "Red is a color. Some more text. I'm out of ideas. 42. What's your name?", - "Colorful")); - model.setItem(1, createItem(Qt::blue, "Blue", "Colorful")); - model.setItem(2, createItem(Qt::yellow, "Yellow", "Colorful")); - - model.setItem(3, createItem(Qt::black, "Black", "Not Colorful")); - model.setItem(4, createItem(Qt::darkGray, "Dark Gray", "Not Colorful")); - model.setItem(5, createItem(Qt::gray, "Gray", "Not Colorful")); - model.setItem(6, createItem(Qt::lightGray, "Light Gray", "Not Colorful")); - model.setItem(7, createItem(Qt::white, "White", "Not Colorful")); - - model.setItem(8, createItem(Qt::darkGreen, "Dark Green", "")); - model.setItem(9, progresser->addTrackedIndex(createItem(Qt::green, "Green", ""))); - - for (int i = 0; i < 20; ++i) - { - model.setItem(i + 10, createItem(i + 1, "Items 1-20")); - } - - GroupedProxyModel pModel; - pModel.setSourceModel(&model); - - GroupView w; - w.setItemDelegate(new ListViewDelegate); - w.setModel(&pModel); - w.resize(640, 480); - w.show(); - - return a.exec(); -} diff --git a/depends/groupview/main.h b/depends/groupview/main.h deleted file mode 100644 index 2a358329..00000000 --- a/depends/groupview/main.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "GroupView.h" - -class Progresser : public QObject -{ - Q_OBJECT -public: - explicit Progresser(QObject *parent = 0) : QObject(parent) - { - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); - timer->start(50); - } - - QStandardItem *addTrackedIndex(QStandardItem *item) - { - item->setData(1000, GroupViewRoles::ProgressMaximumRole); - m_items.append(item); - return item; - } - -public -slots: - void timeout() - { - QList toRemove; - for (auto item : m_items) - { - int maximum = item->data(GroupViewRoles::ProgressMaximumRole).toInt(); - int value = item->data(GroupViewRoles::ProgressValueRole).toInt(); - int newvalue = std::min(value + 3, maximum); - item->setData(newvalue, GroupViewRoles::ProgressValueRole); - - if(newvalue >= maximum) - { - toRemove.append(item); - } - } - for(auto remove : toRemove) - { - m_items.removeAll(remove); - } - } - -private: - QList m_items; -}; diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 9977dc75..cd8ec926 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -37,12 +38,12 @@ #include "userutils.h" #include "pathutils.h" -#include "categorizedview.h" -#include "categorydrawer.h" +#include "gui/groupview/GroupView.h" +#include "gui/groupview/InstanceDelegate.h" #include "gui/Platform.h" -#include "gui/widgets/InstanceDelegate.h" + #include "gui/widgets/LabeledToolButton.h" #include "gui/dialogs/SettingsDialog.h" @@ -140,21 +141,21 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi // Create the instance list widget { - view = new KCategorizedView(ui->centralWidget); - drawer = new KCategoryDrawer(view); + view = new GroupView(ui->centralWidget); + view->setSelectionMode(QAbstractItemView::SingleSelection); - view->setCategoryDrawer(drawer); - view->setCollapsibleBlocks(true); - view->setViewMode(QListView::IconMode); - view->setFlow(QListView::LeftToRight); - view->setWordWrap(true); - view->setMouseTracking(true); - view->viewport()->setAttribute(Qt::WA_Hover); + // view->setCategoryDrawer(drawer); + // view->setCollapsibleBlocks(true); + // view->setViewMode(QListView::IconMode); + // view->setFlow(QListView::LeftToRight); + // view->setWordWrap(true); + // view->setMouseTracking(true); + // view->viewport()->setAttribute(Qt::WA_Hover); auto delegate = new ListViewDelegate(); view->setItemDelegate(delegate); - view->setSpacing(10); - view->setUniformItemWidths(true); + // view->setSpacing(10); + // view->setUniformItemWidths(true); // do not show ugly blue border on the mac view->setAttribute(Qt::WA_MacShowFocusRect, false); @@ -331,7 +332,6 @@ MainWindow::~MainWindow() { delete ui; delete proxymodel; - delete drawer; } void MainWindow::showInstanceContextMenu(const QPoint &pos) diff --git a/gui/MainWindow.h b/gui/MainWindow.h index eeba2c26..4d9e165d 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -27,9 +27,6 @@ class QToolButton; class LabeledToolButton; class QLabel; -class InstanceProxyModel; -class KCategorizedView; -class KCategoryDrawer; class MinecraftProcess; class ConsoleWindow; @@ -185,8 +182,7 @@ protected: private: Ui::MainWindow *ui; - KCategoryDrawer *drawer; - KCategorizedView *view; + class GroupView *view; InstanceProxyModel *proxymodel; MinecraftProcess *proc; ConsoleWindow *console; diff --git a/gui/dialogs/IconPickerDialog.cpp b/gui/dialogs/IconPickerDialog.cpp index f7970b37..9b1c26ff 100644 --- a/gui/dialogs/IconPickerDialog.cpp +++ b/gui/dialogs/IconPickerDialog.cpp @@ -23,7 +23,7 @@ #include "ui_IconPickerDialog.h" #include "gui/Platform.h" -#include "gui/widgets/InstanceDelegate.h" +#include "gui/groupview/InstanceDelegate.h" #include "logic/icons/IconList.h" diff --git a/gui/groupview/Group.cpp b/gui/groupview/Group.cpp new file mode 100644 index 00000000..f216cc6e --- /dev/null +++ b/gui/groupview/Group.cpp @@ -0,0 +1,169 @@ +#include "Group.h" + +#include +#include +#include + +#include "GroupView.h" + +Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) +{ +} + +Group::Group(const Group *other) + : view(other->view), text(other->text), collapsed(other->collapsed) +{ +} + +void Group::update() +{ + firstItemIndex = firstItem().row(); + + rowHeights = QVector(numRows()); + for (int i = 0; i < numRows(); ++i) + { + rowHeights[i] = view->categoryRowHeight( + view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0)); + } +} + +Group::HitResults Group::hitScan(const QPoint &pos) const +{ + Group::HitResults results = Group::NoHit; + int y_start = verticalPosition(); + int body_start = y_start + headerHeight(); + int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5? + int y = pos.y(); + // int x = pos.x(); + if(y < y_start) + { + results = Group::NoHit; + } + else if(y < body_start) + { + results = Group::HeaderHit; + int collapseSize = headerHeight() - 4; + + // the icon + QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); + if(iconRect.contains(pos)) + { + results |= Group::CheckboxHit; + } + } + else if (y < body_end) + { + results |= Group::BodyHit; + } + return results; +} + +void Group::drawHeader(QPainter *painter, const int y) +{ + painter->save(); + + int height = headerHeight() - 4; + int collapseSize = height; + + // the icon + QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y, collapseSize, collapseSize); + painter->setPen(QPen(Qt::black, 1)); + painter->drawRect(iconRect); + static const int margin = 2; + QRect iconSubrect = iconRect.adjusted(margin, margin, -margin, -margin); + int midX = iconSubrect.center().x(); + int midY = iconSubrect.center().y(); + if (collapsed) + { + painter->drawLine(midX, iconSubrect.top(), midX, iconSubrect.bottom()); + } + painter->drawLine(iconSubrect.left(), midY, iconSubrect.right(), midY); + + // the text + int textWidth = painter->fontMetrics().width(text); + QRect textRect = QRect(iconRect.right() + 4, y, textWidth, headerHeight()); + painter->setBrush(view->viewOptions().palette.text()); + view->style()->drawItemText(painter, textRect, Qt::AlignHCenter | Qt::AlignVCenter, + view->viewport()->palette(), true, text); + + // the line + painter->drawLine(textRect.right() + 4, y + headerHeight() / 2, + view->contentWidth() - view->m_rightMargin, y + headerHeight() / 2); + + painter->restore(); +} + +int Group::totalHeight() const +{ + return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'? +} + +int Group::headerHeight() const +{ + return view->viewport()->fontMetrics().height() + 4; +} + +int Group::contentHeight() const +{ + if (collapsed) + { + return 0; + } + int result = 0; + for (int i = 0; i < rowHeights.size(); ++i) + { + result += rowHeights[i]; + } + return result; +} + +int Group::numRows() const +{ + return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow())); +} + +int Group::verticalPosition() const +{ + int res = 0; + const QList cats = view->m_groups; + for (int i = 0; i < cats.size(); ++i) + { + if (cats.at(i) == this) + { + break; + } + res += cats.at(i)->totalHeight() + view->m_categoryMargin; + } + return res; +} + +QList Group::items() const +{ + QList indices; + for (int i = 0; i < view->model()->rowCount(); ++i) + { + const QModelIndex index = view->model()->index(i, 0); + if (index.data(GroupViewRoles::GroupRole).toString() == text) + { + indices.append(index); + } + } + return indices; +} + +int Group::numItems() const +{ + return items().size(); +} + +QModelIndex Group::firstItem() const +{ + QList indices = items(); + return indices.isEmpty() ? QModelIndex() : indices.first(); +} + +QModelIndex Group::lastItem() const +{ + QList indices = items(); + return indices.isEmpty() ? QModelIndex() : indices.last(); +} diff --git a/gui/groupview/Group.h b/gui/groupview/Group.h new file mode 100644 index 00000000..455ee1a8 --- /dev/null +++ b/gui/groupview/Group.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +class GroupView; +class QPainter; +class QModelIndex; + +struct Group +{ +/* constructors */ + Group(const QString &text, GroupView *view); + Group(const Group *other); + +/* data */ + GroupView *view; + QString text; + bool collapsed; + QVector rowHeights; + int firstItemIndex; + +/* logic */ + /// do stuff. and things. TODO: redo. + void update(); + + /// draw the header at y-position. + void drawHeader(QPainter *painter, const int y); + + /// height of the group, in total. includes a small bit of padding. + int totalHeight() const; + + /// height of the group header, in pixels + int headerHeight() const; + + /// height of the group content, in pixels + int contentHeight() const; + + /// the number of visual rows this group has + int numRows() const; + + /// the height at which this group starts, in pixels + int verticalPosition() const; + + enum HitResult + { + NoHit = 0x0, + TextHit = 0x1, + CheckboxHit = 0x2, + HeaderHit = 0x4, + BodyHit = 0x8 + }; + Q_DECLARE_FLAGS(HitResults, HitResult) + + /// shoot! BANG! what did we hit? + HitResults hitScan (const QPoint &pos) const; + + /// super derpy thing. + QList items() const; + /// I don't even + int numItems() const; + QModelIndex firstItem() const; + QModelIndex lastItem() const; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults) diff --git a/gui/groupview/GroupView.cpp b/gui/groupview/GroupView.cpp new file mode 100644 index 00000000..89e3e223 --- /dev/null +++ b/gui/groupview/GroupView.cpp @@ -0,0 +1,908 @@ +#include "GroupView.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Group.h" + +template bool listsIntersect(const QList &l1, const QList t2) +{ + for (auto &item : l1) + { + if (t2.contains(item)) + { + return true; + } + } + return false; +} + +GroupView::GroupView(QWidget *parent) + : QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), + m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) +{ + // setViewMode(IconMode); + // setMovement(Snap); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + // setWordWrap(true); + // setDragDropMode(QListView::InternalMove); + setAcceptDrops(true); + m_spacing = 5; +} + +GroupView::~GroupView() +{ + qDeleteAll(m_groups); + m_groups.clear(); +} + +void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QVector &roles) +{ + if (roles.contains(GroupViewRoles::GroupRole) || roles.contains(Qt::DisplayRole)) + { + updateGeometries(); + } + viewport()->update(); +} +void GroupView::rowsInserted(const QModelIndex &parent, int start, int end) +{ + updateGeometries(); + viewport()->update(); +} + +void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + updateGeometries(); + viewport()->update(); +} + +void GroupView::updateGeometries() +{ + int previousScroll = verticalScrollBar()->value(); + + QMap cats; + + for (int i = 0; i < model()->rowCount(); ++i) + { + const QString groupName = + model()->index(i, 0).data(GroupViewRoles::GroupRole).toString(); + if (!cats.contains(groupName)) + { + Group *old = this->category(groupName); + if (old) + { + cats.insert(groupName, new Group(old)); + } + else + { + cats.insert(groupName, new Group(groupName, this)); + } + } + } + + /*if (m_editedCategory) + { + m_editedCategory = cats[m_editedCategory->text]; + }*/ + + qDeleteAll(m_groups); + m_groups = cats.values(); + + for (auto cat : m_groups) + { + cat->update(); + } + + if (m_groups.isEmpty()) + { + verticalScrollBar()->setRange(0, 0); + } + else + { + int totalHeight = 0; + for (auto category : m_groups) + { + totalHeight += category->totalHeight() + m_categoryMargin; + } + // remove the last margin (we don't want it) + totalHeight -= m_categoryMargin; + totalHeight += m_bottomMargin; + verticalScrollBar()->setRange(0, totalHeight - height()); + } + + verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum())); + + viewport()->update(); +} + +bool GroupView::isIndexHidden(const QModelIndex &index) const +{ + Group *cat = category(index); + if (cat) + { + return cat->collapsed; + } + else + { + return false; + } +} + +Group *GroupView::category(const QModelIndex &index) const +{ + return category(index.data(GroupViewRoles::GroupRole).toString()); +} + +Group *GroupView::category(const QString &cat) const +{ + for (auto group : m_groups) + { + if (group->text == cat) + { + return group; + } + } + return nullptr; +} + +Group *GroupView::categoryAt(const QPoint &pos) const +{ + for (auto group : m_groups) + { + if(group->hitScan(pos) & Group::CheckboxHit) + { + return group; + } + } + return nullptr; +} + +int GroupView::itemsPerRow() const +{ + return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing)); +} + +int GroupView::contentWidth() const +{ + return width() - m_leftMargin - m_rightMargin; +} + +int GroupView::itemWidth() const +{ + return itemDelegate() + ->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0)) + .width(); +} + +int GroupView::categoryRowHeight(const QModelIndex &index) const +{ + QModelIndexList indices; + int internalRow = categoryInternalPosition(index).second; + for (auto &i : category(index)->items()) + { + if (categoryInternalPosition(i).second == internalRow) + { + indices.append(i); + } + } + + int largestHeight = 0; + for (auto &i : indices) + { + largestHeight = + qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); + } + return largestHeight; +} + +QPair GroupView::categoryInternalPosition(const QModelIndex &index) const +{ + QList indices = category(index)->items(); + int x = 0; + int y = 0; + const int perRow = itemsPerRow(); + for (int i = 0; i < indices.size(); ++i) + { + if (indices.at(i) == index) + { + break; + } + ++x; + if (x == perRow) + { + x = 0; + ++y; + } + } + return qMakePair(x, y); +} + +int GroupView::categoryInternalRowTop(const QModelIndex &index) const +{ + Group *cat = category(index); + int categoryInternalRow = categoryInternalPosition(index).second; + int result = 0; + for (int i = 0; i < categoryInternalRow; ++i) + { + result += cat->rowHeights.at(i); + } + return result; +} + +int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const +{ + for (auto &i : category->items()) + { + QPair pos = categoryInternalPosition(i); + if (pos.second == internalRow) + { + return categoryRowHeight(i); + } + } + return -1; +} + +void GroupView::mousePressEvent(QMouseEvent *event) +{ + // endCategoryEditor(); + + QPoint pos = event->pos() + offset(); + QPersistentModelIndex index = indexAt(pos); + + m_pressedIndex = index; + m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); + QItemSelectionModel::SelectionFlags selection_flags = selectionCommand(index, event); + m_pressedPosition = pos; + + m_pressedCategory = categoryAt(m_pressedPosition); + if (m_pressedCategory) + { + setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); + event->accept(); + return; + } + + if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) + { + // we disable scrollTo for mouse press so the item doesn't change position + // when the user is interacting with it (ie. clicking on it) + bool autoScroll = hasAutoScroll(); + setAutoScroll(false); + selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + setAutoScroll(autoScroll); + QRect rect(m_pressedPosition, pos); + setSelection(rect, QItemSelectionModel::ClearAndSelect); + + // signal handlers may change the model + emit pressed(index); + } + else + { + // Forces a finalize() even if mouse is pressed, but not on a item + selectionModel()->select(QModelIndex(), QItemSelectionModel::Select); + } +} + +void GroupView::mouseMoveEvent(QMouseEvent *event) +{ + QPoint topLeft; + QPoint pos = event->pos() + offset(); + + if (state() == ExpandingState || state() == CollapsingState) + { + return; + } + + if (state() == DraggingState) + { + topLeft = m_pressedPosition - offset(); + if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) + { + m_pressedIndex = QModelIndex(); + startDrag(model()->supportedDragActions()); + setState(NoState); + stopAutoScroll(); + } + return; + } + + if (selectionMode() != SingleSelection) + { + topLeft = m_pressedPosition - offset(); + } + else + { + topLeft = pos; + } + + if (m_pressedIndex.isValid() && (state() != DragSelectingState) && + (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty()) + { + setState(DraggingState); + return; + } + + if ((event->buttons() & Qt::LeftButton) && selectionModel()) + { + setState(DragSelectingState); + + setSelection(QRect(pos, pos), QItemSelectionModel::ClearAndSelect); + QModelIndex index = indexAt(pos); + + // set at the end because it might scroll the view + if (index.isValid() && (index != selectionModel()->currentIndex()) && + (index.flags() & Qt::ItemIsEnabled)) + { + selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + } + } +} + +void GroupView::mouseReleaseEvent(QMouseEvent *event) +{ + QPoint pos = event->pos() + offset(); + QPersistentModelIndex index = indexAt(pos); + + bool click = (index == m_pressedIndex && index.isValid()) || + (m_pressedCategory && m_pressedCategory == categoryAt(pos)); + + if (click && m_pressedCategory) + { + if (state() == ExpandingState) + { + m_pressedCategory->collapsed = false; + updateGeometries(); + viewport()->update(); + event->accept(); + return; + } + else if (state() == CollapsingState) + { + m_pressedCategory->collapsed = true; + updateGeometries(); + viewport()->update(); + event->accept(); + return; + } + } + + m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; + + setState(NoState); + + if (click) + { + if (event->button() == Qt::LeftButton) + { + emit clicked(index); + } + QStyleOptionViewItem option = viewOptions(); + if (m_pressedAlreadySelected) + { + option.state |= QStyle::State_Selected; + } + if ((model()->flags(index) & Qt::ItemIsEnabled) && + style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) + { + emit activated(index); + } + } +} + +void GroupView::mouseDoubleClickEvent(QMouseEvent *event) +{ + QModelIndex index = indexAt(event->pos()); + if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index)) + { + QMouseEvent me(QEvent::MouseButtonPress, event->localPos(), event->windowPos(), + event->screenPos(), event->button(), event->buttons(), + event->modifiers()); + mousePressEvent(&me); + return; + } + // signal handlers may change the model + QPersistentModelIndex persistent = index; + emit doubleClicked(persistent); +} + +void GroupView::paintEvent(QPaintEvent *event) +{ + QPainter painter(this->viewport()); + + int y = -verticalOffset(); + for (int i = 0; i < m_groups.size(); ++i) + { + Group *category = m_groups.at(i); + category->drawHeader(&painter, y); + y += category->totalHeight() + m_categoryMargin; + } + + for (int i = 0; i < model()->rowCount(); ++i) + { + const QModelIndex index = model()->index(i, 0); + if (isIndexHidden(index)) + { + continue; + } + Qt::ItemFlags flags = index.flags(); + QStyleOptionViewItemV4 option(viewOptions()); + option.rect = visualRect(index); + option.widget = this; + option.features |= + QStyleOptionViewItemV2::WrapText; // FIXME: what is the meaning of this anyway? + if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) + { + option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected + : QStyle::State_None; + } + else + { + option.state &= ~QStyle::State_Selected; + } + option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None; + if (!(flags & Qt::ItemIsEnabled)) + { + option.state &= ~QStyle::State_Enabled; + } + itemDelegate()->paint(&painter, option, index); + } + + /* + * Drop indicators for manual reordering... + */ +#if 0 + if (!m_lastDragPosition.isNull()) + { + QPair pair = rowDropPos(m_lastDragPosition); + Group *category = pair.first; + int row = pair.second; + if (category) + { + int internalRow = row - category->firstItemIndex; + QLine line; + if (internalRow >= category->numItems()) + { + QRect toTheRightOfRect = visualRect(category->lastItem()); + line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight()); + } + else + { + QRect toTheLeftOfRect = visualRect(model()->index(row, 0)); + line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft()); + } + painter.save(); + painter.setPen(QPen(Qt::black, 3)); + painter.drawLine(line); + painter.restore(); + } + } +#endif +} + +void GroupView::resizeEvent(QResizeEvent *event) +{ + // QListView::resizeEvent(event); + + // if (m_categoryEditor) + // { + // m_categoryEditor->resize(qMax(contentWidth() / 2, + // m_editedCategory->textRect.width()), + // m_categoryEditor->height()); + // } + + updateGeometries(); +} + +void GroupView::dragEnterEvent(QDragEnterEvent *event) +{ + if (!isDragEventAccepted(event)) + { + return; + } + m_lastDragPosition = event->pos() + offset(); + viewport()->update(); + event->accept(); +} + +void GroupView::dragMoveEvent(QDragMoveEvent *event) +{ + if (!isDragEventAccepted(event)) + { + return; + } + m_lastDragPosition = event->pos() + offset(); + viewport()->update(); + event->accept(); +} + +void GroupView::dragLeaveEvent(QDragLeaveEvent *event) +{ + m_lastDragPosition = QPoint(); + viewport()->update(); +} + +void GroupView::dropEvent(QDropEvent *event) +{ + m_lastDragPosition = QPoint(); + + stopAutoScroll(); + setState(NoState); + + if (event->source() != this || !(event->possibleActions() & Qt::MoveAction)) + { + return; + } + + QPair dropPos = rowDropPos(event->pos() + offset()); + const Group *category = dropPos.first; + const int row = dropPos.second; + + if (row == -1) + { + viewport()->update(); + return; + } + + const QString categoryText = category->text; + if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) + { + model()->setData(model()->index(row, 0), categoryText, + GroupViewRoles::GroupRole); + event->setDropAction(Qt::MoveAction); + event->accept(); + } + updateGeometries(); + viewport()->update(); +} + +void GroupView::startDrag(Qt::DropActions supportedActions) +{ + QModelIndexList indexes = selectionModel()->selectedIndexes(); + if (indexes.count() > 0) + { + QMimeData *data = model()->mimeData(indexes); + if (!data) + { + return; + } + QRect rect; + QPixmap pixmap = renderToPixmap(indexes, &rect); + //rect.translate(offset()); + // rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); + QDrag *drag = new QDrag(this); + drag->setPixmap(pixmap); + drag->setMimeData(data); + drag->setHotSpot(m_pressedPosition - rect.topLeft()); + Qt::DropAction defaultDropAction = Qt::IgnoreAction; + if (this->defaultDropAction() != Qt::IgnoreAction && + (supportedActions & this->defaultDropAction())) + { + defaultDropAction = this->defaultDropAction(); + } + if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) + { + const QItemSelection selection = selectionModel()->selection(); + + for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) + { + QModelIndex parent = (*it).parent(); + if ((*it).left() != 0) + { + continue; + } + if ((*it).right() != (model()->columnCount(parent) - 1)) + { + continue; + } + int count = (*it).bottom() - (*it).top() + 1; + model()->removeRows((*it).top(), count, parent); + } + } + } +} + +QRect GroupView::visualRect(const QModelIndex &index) const +{ + return geometryRect(index).translated(-offset()); +} + +QRect GroupView::geometryRect(const QModelIndex &index) const +{ + if (!index.isValid() || isIndexHidden(index) || index.column() > 0) + { + return QRect(); + } + + const Group *cat = category(index); + QPair pos = categoryInternalPosition(index); + int x = pos.first; + // int y = pos.second; + + QRect out; + out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); + out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); + out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); + + return out; +} + +/* +void CategorizedView::startCategoryEditor(Category *category) +{ + if (m_categoryEditor != 0) + { + return; + } + m_editedCategory = category; + m_categoryEditor = new QLineEdit(m_editedCategory->text, this); + QRect rect = m_editedCategory->textRect; + rect.setWidth(qMax(contentWidth() / 2, rect.width())); + m_categoryEditor->setGeometry(rect); + m_categoryEditor->show(); + m_categoryEditor->setFocus(); + connect(m_categoryEditor, &QLineEdit::returnPressed, this, +&CategorizedView::endCategoryEditor); +} + +void CategorizedView::endCategoryEditor() +{ + if (m_categoryEditor == 0) + { + return; + } + m_editedCategory->text = m_categoryEditor->text(); + m_updatesDisabled = true; + foreach (const QModelIndex &index, itemsForCategory(m_editedCategory)) + { + const_cast(index.model())->setData(index, +m_categoryEditor->text(), CategoryRole); + } + m_updatesDisabled = false; + delete m_categoryEditor; + m_categoryEditor = 0; + m_editedCategory = 0; + updateGeometries(); +} +*/ + +QModelIndex GroupView::indexAt(const QPoint &point) const +{ + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + if (geometryRect(index).contains(point)) + { + return index; + } + } + return QModelIndex(); +} + +void GroupView::setSelection(const QRect &rect, + const QItemSelectionModel::SelectionFlags commands) +{ + for (int i = 0; i < model()->rowCount(); ++i) + { + QModelIndex index = model()->index(i, 0); + QRect itemRect = geometryRect(index); + if (itemRect.intersects(rect)) + { + selectionModel()->select(index, commands); + update(itemRect.translated(-offset())); + } + } + +} + +QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const +{ + Q_ASSERT(r); + auto paintPairs = draggablePaintPairs(indices, r); + if (paintPairs.isEmpty()) + { + return QPixmap(); + } + QPixmap pixmap(r->size()); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + QStyleOptionViewItem option = viewOptions(); + option.state |= QStyle::State_Selected; + for (int j = 0; j < paintPairs.count(); ++j) + { + option.rect = paintPairs.at(j).first.translated(-r->topLeft()); + const QModelIndex ¤t = paintPairs.at(j).second; + itemDelegate()->paint(&painter, option, current); + } + return pixmap; +} + +QList> GroupView::draggablePaintPairs(const QModelIndexList &indices, + QRect *r) const +{ + Q_ASSERT(r); + QRect &rect = *r; + QList> ret; + for (int i = 0; i < indices.count(); ++i) + { + const QModelIndex &index = indices.at(i); + const QRect current = geometryRect(index); + ret += qMakePair(current, index); + rect |= current; + } + return ret; +} + +bool GroupView::isDragEventAccepted(QDropEvent *event) +{ + if (event->source() != this) + { + return false; + } + if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes())) + { + return false; + } + if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), + rowDropPos(event->pos()).second, 0, QModelIndex())) + { + return false; + } + return true; +} + +QPair GroupView::rowDropPos(const QPoint &pos) +{ + // check that we aren't on a category header and calculate which category we're in + Group *category = 0; + { + int y = 0; + for (auto cat : m_groups) + { + if (pos.y() > y && pos.y() < (y + cat->headerHeight())) + { + return qMakePair(nullptr, -1); + } + y += cat->totalHeight() + m_categoryMargin; + if (pos.y() < y) + { + category = cat; + break; + } + } + if (category == 0) + { + return qMakePair(nullptr, -1); + } + } + + QList indices = category->items(); + + // calculate the internal column + int internalColumn = -1; + { + const int itemWidth = this->itemWidth(); + if (pos.x() >= (itemWidth * itemsPerRow())) + { + internalColumn = itemsPerRow(); + } + else + { + for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c) + { + if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2)) + { + internalColumn = c; + break; + } + } + } + if (internalColumn == -1) + { + return qMakePair(nullptr, -1); + } + } + + // calculate the internal row + int internalRow = -1; + { + // FIXME rework the drag and drop code + const int top = category->verticalPosition(); + for (int r = 0, h = top; r < category->numRows(); + h += itemHeightForCategoryRow(category, r), ++r) + { + if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r))) + { + internalRow = r; + break; + } + } + if (internalRow == -1) + { + return qMakePair(nullptr, -1); + } + // this happens if we're in the margin between a one category and another + // categories header + if (internalRow > (indices.size() / itemsPerRow())) + { + return qMakePair(nullptr, -1); + } + } + + // flaten the internalColumn/internalRow to one row + int categoryRow = internalRow * itemsPerRow() + internalColumn; + + // this is used if we're past the last item + if (categoryRow >= indices.size()) + { + return qMakePair(category, indices.last().row() + 1); + } + + return qMakePair(category, indices.at(categoryRow).row()); +} + +QPoint GroupView::offset() const +{ + return QPoint(horizontalOffset(), verticalOffset()); +} + +QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) const +{ + QRegion region; + for (auto &range : selection) + { + int start_row = range.top(); + int end_row = range.bottom(); + for (int row = start_row; row <= end_row; ++row) + { + int start_column = range.left(); + int end_column = range.right(); + for (int column = start_column; column <= end_column; ++column) + { + QModelIndex index = model()->index(row, column, rootIndex()); + region += visualRect(index); // OK + } + } + } + return region; +} +QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction, + Qt::KeyboardModifiers modifiers) +{ + auto current = currentIndex(); + if(!current.isValid()) + { + qDebug() << "model row: invalid"; + return current; + } + qDebug() << "model row: " << current.row(); + auto cat = category(current); + int i = m_groups.indexOf(cat); + if(i >= 0) + { + // this is a pile of something foul + auto real_group = m_groups[i]; + int beginning_row = 0; + for(auto group: m_groups) + { + if(group == real_group) + break; + beginning_row += group->numRows(); + } + qDebug() << "category: " << real_group->text; + QPair pos = categoryInternalPosition(current); + int row = beginning_row + pos.second; + qDebug() << "row: " << row; + qDebug() << "column: " << pos.first; + } + return current; +} diff --git a/gui/groupview/GroupView.h b/gui/groupview/GroupView.h new file mode 100644 index 00000000..329a3503 --- /dev/null +++ b/gui/groupview/GroupView.h @@ -0,0 +1,139 @@ +#pragma once + +#include +#include +#include + +struct GroupViewRoles +{ + enum + { + GroupRole = Qt::UserRole, + ProgressValueRole, + ProgressMaximumRole + }; +}; + +struct Group; + +class GroupView : public QAbstractItemView +{ + Q_OBJECT + +public: + GroupView(QWidget *parent = 0); + ~GroupView(); + + QRect geometryRect(const QModelIndex &index) const; + virtual QRect visualRect(const QModelIndex &index) const override; + QModelIndex indexAt(const QPoint &point) const; + void setSelection(const QRect &rect, + const QItemSelectionModel::SelectionFlags commands) override; + + virtual int horizontalOffset() const override + { + return horizontalScrollBar()->value(); + } + + virtual int verticalOffset() const override + { + return verticalScrollBar()->value(); + } + + virtual void scrollContentsBy(int dx, int dy) override + { + scrollDirtyRegion(dx, dy); + viewport()->scroll(dx, dy); + } + + /* + * TODO! + */ + virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override + { + return; + } + + virtual QModelIndex moveCursor(CursorAction cursorAction, + Qt::KeyboardModifiers modifiers) override; + + virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; + +protected +slots: + virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QVector &roles) override; + virtual void rowsInserted(const QModelIndex &parent, int start, int end) override; + virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override; + virtual void updateGeometries() override; + +protected: + virtual bool isIndexHidden(const QModelIndex &index) const override; + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseDoubleClickEvent(QMouseEvent *event) override; + void paintEvent(QPaintEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + + void dragEnterEvent(QDragEnterEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent *event) override; + + void startDrag(Qt::DropActions supportedActions) override; + +private: + friend struct Group; + + QList m_groups; + + int m_leftMargin; + int m_rightMargin; + int m_bottomMargin; + int m_categoryMargin; + + // bool m_updatesDisabled; + + Group *category(const QModelIndex &index) const; + Group *category(const QString &cat) const; + Group *categoryAt(const QPoint &pos) const; + + int itemsPerRow() const; + int contentWidth() const; + +private: + int itemWidth() const; + int categoryRowHeight(const QModelIndex &index) const; + + /*QLineEdit *m_categoryEditor; + Category *m_editedCategory; + void startCategoryEditor(Category *category); + +private slots: + void endCategoryEditor();*/ + +private: /* variables */ + QPoint m_pressedPosition; + QPersistentModelIndex m_pressedIndex; + bool m_pressedAlreadySelected; + Group *m_pressedCategory; + QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; + QPoint m_lastDragPosition; + int m_spacing = 5; + +private: /* methods */ + QPair categoryInternalPosition(const QModelIndex &index) const; + int categoryInternalRowTop(const QModelIndex &index) const; + int itemHeightForCategoryRow(const Group *category, const int internalRow) const; + + QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; + QList> draggablePaintPairs(const QModelIndexList &indices, + QRect *r) const; + + bool isDragEventAccepted(QDropEvent *event); + + QPair rowDropPos(const QPoint &pos); + + QPoint offset() const; +}; diff --git a/gui/groupview/GroupedProxyModel.cpp b/gui/groupview/GroupedProxyModel.cpp new file mode 100644 index 00000000..d9d6ac78 --- /dev/null +++ b/gui/groupview/GroupedProxyModel.cpp @@ -0,0 +1,26 @@ +#include "GroupedProxyModel.h" + +#include "GroupView.h" + +GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ +} + +bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + const QString leftCategory = left.data(GroupViewRoles::GroupRole).toString(); + const QString rightCategory = right.data(GroupViewRoles::GroupRole).toString(); + if (leftCategory == rightCategory) + { + return subSortLessThan(left, right); + } + else + { + return leftCategory < rightCategory; + } +} + +bool GroupedProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const +{ + return left.row() < right.row(); +} diff --git a/gui/groupview/GroupedProxyModel.h b/gui/groupview/GroupedProxyModel.h new file mode 100644 index 00000000..12edee0f --- /dev/null +++ b/gui/groupview/GroupedProxyModel.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class GroupedProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + GroupedProxyModel(QObject *parent = 0); + +protected: + virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const; +}; diff --git a/gui/groupview/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp new file mode 100644 index 00000000..8527e2bc --- /dev/null +++ b/gui/groupview/InstanceDelegate.cpp @@ -0,0 +1,281 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InstanceDelegate.h" +#include +#include +#include +#include +#include + +#include "GroupView.h" + +// Origin: Qt +static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, + qreal &widthUsed) +{ + height = 0; + widthUsed = 0; + textLayout.beginLayout(); + QString str = textLayout.text(); + while (true) + { + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + break; + if (line.textLength() == 0) + break; + line.setLineWidth(lineWidth); + line.setPosition(QPointF(0, height)); + height += line.height(); + widthUsed = qMax(widthUsed, line.naturalTextWidth()); + } + textLayout.endLayout(); +} + +#define QFIXED_MAX (INT_MAX / 256) + +ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) +{ +} + +void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, + const QRect &rect) +{ + if ((option.state & QStyle::State_Selected)) + painter->fillRect(rect, option.palette.brush(QPalette::Highlight)); + else + { + QColor backgroundColor = option.palette.color(QPalette::Background); + backgroundColor.setAlpha(160); + painter->fillRect(rect, QBrush(backgroundColor)); + } +} + +void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect) +{ + if (!(option.state & QStyle::State_HasFocus)) + return; + QStyleOptionFocusRect opt; + opt.direction = option.direction; + opt.fontMetrics = option.fontMetrics; + opt.palette = option.palette; + opt.rect = rect; + // opt.state = option.state | QStyle::State_KeyboardFocusChange | + // QStyle::State_Item; + auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base; + opt.backgroundColor = option.palette.color(col); + // Apparently some widget styles expect this hint to not be set + painter->setRenderHint(QPainter::Antialiasing, false); + + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + + style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); + + painter->setRenderHint(QPainter::Antialiasing); +} + +// TODO this can be made a lot prettier +void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option, + const int value, const int maximum) +{ + if (maximum == 0 || value == maximum) + { + return; + } + + painter->save(); + + qreal percent = (qreal)value / (qreal)maximum; + QColor color = option.palette.color(QPalette::Dark); + color.setAlphaF(0.70f); + painter->setBrush(color); + painter->setPen(QPen(QBrush(), 0)); + painter->drawPie(option.rect, 90 * 16, -percent * 360 * 16); + + painter->restore(); +} + +static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option) +{ + QStyle *style = option->widget ? option->widget->style() : QApplication::style(); + QTextOption textOption; + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + QTextLayout textLayout; + textLayout.setTextOption(textOption); + textLayout.setFont(option->font); + textLayout.setText(option->text); + const int textMargin = + style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1; + QRect bounds(0, 0, 100 - 2 * textMargin, 600); + qreal height = 0, widthUsed = 0; + viewItemTextLayout(textLayout, bounds.width(), height, widthUsed); + const QSize size(qCeil(widthUsed), qCeil(height)); + return QSize(size.width() + 2 * textMargin, size.height()); +} + +void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + painter->save(); + painter->setClipRect(opt.rect); + + opt.features |= QStyleOptionViewItem::WrapText; + opt.text = index.data().toString(); + opt.textElideMode = Qt::ElideRight; + opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; + + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + + // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); + const int iconSize = 48; + QRect iconbox = opt.rect; + const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; + QRect textRect = opt.rect; + QRect textHighlightRect = textRect; + // clip the decoration on top, remove width padding + textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0); + + textHighlightRect.adjust(0, iconSize + 5, 0, 0); + + // draw background + { + // FIXME: unused + // QSize textSize = viewItemTextSize ( &opt ); + QPalette::ColorGroup cg; + QStyleOptionViewItemV4 opt2(opt); + + if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) + { + if (!(opt.state & QStyle::State_Active)) + cg = QPalette::Inactive; + else + cg = QPalette::Normal; + } + else + { + cg = QPalette::Disabled; + } + opt2.palette.setCurrentColorGroup(cg); + + // fill in background, if any + if (opt.backgroundBrush.style() != Qt::NoBrush) + { + QPointF oldBO = painter->brushOrigin(); + painter->setBrushOrigin(opt.rect.topLeft()); + painter->fillRect(opt.rect, opt.backgroundBrush); + painter->setBrushOrigin(oldBO); + } + + if (opt.showDecorationSelected) + { + drawSelectionRect(painter, opt2, opt.rect); + drawFocusRect(painter, opt2, opt.rect); + // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); + } + else + { + + // if ( opt.state & QStyle::State_Selected ) + { + // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, + // opt.widget ); + // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, + // QPalette::Highlight ) ); + drawSelectionRect(painter, opt2, textHighlightRect); + drawFocusRect(painter, opt2, textHighlightRect); + } + } + } + + // draw the icon + { + QIcon::Mode mode = QIcon::Normal; + if (!(opt.state & QStyle::State_Enabled)) + mode = QIcon::Disabled; + else if (opt.state & QStyle::State_Selected) + mode = QIcon::Selected; + QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; + + iconbox.setHeight(iconSize); + opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); + } + // set the text colors + QPalette::ColorGroup cg = + opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) + cg = QPalette::Inactive; + if (opt.state & QStyle::State_Selected) + { + painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); + } + else + { + painter->setPen(opt.palette.color(cg, QPalette::Text)); + } + + // draw the text + QTextOption textOption; + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + textOption.setTextDirection(opt.direction); + textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment)); + QTextLayout textLayout; + textLayout.setTextOption(textOption); + textLayout.setFont(opt.font); + textLayout.setText(opt.text); + + qreal width, height; + viewItemTextLayout(textLayout, textRect.width(), height, width); + + const int lineCount = textLayout.lineCount(); + + const QRect layoutRect = QStyle::alignedRect( + opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); + const QPointF position = layoutRect.topLeft(); + for (int i = 0; i < lineCount; ++i) + { + const QTextLine line = textLayout.lineAt(i); + line.draw(painter, position); + } + + drawProgressOverlay(painter, opt, + index.data(GroupViewRoles::ProgressValueRole).toInt(), + index.data(GroupViewRoles::ProgressMaximumRole).toInt()); + + painter->restore(); +} + +QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + opt.features |= QStyleOptionViewItem::WrapText; + opt.text = index.data().toString(); + opt.textElideMode = Qt::ElideRight; + opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; + + QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); + const int textMargin = + style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; + int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables + QSize szz = viewItemTextSize(&opt); + height += szz.height(); + // FIXME: maybe the icon items could scale and keep proportions? + QSize sz(100, height); + return sz; +} diff --git a/gui/groupview/InstanceDelegate.h b/gui/groupview/InstanceDelegate.h new file mode 100644 index 00000000..de2f429b --- /dev/null +++ b/gui/groupview/InstanceDelegate.h @@ -0,0 +1,29 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +class ListViewDelegate : public QStyledItemDelegate +{ +public: + explicit ListViewDelegate(QObject *parent = 0); + +protected: + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; +}; diff --git a/gui/widgets/InstanceDelegate.cpp b/gui/widgets/InstanceDelegate.cpp deleted file mode 100644 index 33da7130..00000000 --- a/gui/widgets/InstanceDelegate.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InstanceDelegate.h" -#include -#include -#include -#include -#include -#include "Common.h" -#define QFIXED_MAX (INT_MAX / 256) - -ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) -{ -} - -void drawSelectionRect(QPainter *painter, const QStyleOptionViewItemV4 &option, - const QRect &rect) -{ - if ((option.state & QStyle::State_Selected)) - painter->fillRect(rect, option.palette.brush(QPalette::Highlight)); - else - { - QColor backgroundColor = option.palette.color(QPalette::Background); - backgroundColor.setAlpha(160); - painter->fillRect(rect, QBrush(backgroundColor)); - } -} - -void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect) -{ - if (!(option.state & QStyle::State_HasFocus)) - return; - QStyleOptionFocusRect opt; - opt.direction = option.direction; - opt.fontMetrics = option.fontMetrics; - opt.palette = option.palette; - opt.rect = rect; - // opt.state = option.state | QStyle::State_KeyboardFocusChange | - // QStyle::State_Item; - auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base; - opt.backgroundColor = option.palette.color(col); - // Apparently some widget styles expect this hint to not be set - painter->setRenderHint(QPainter::Antialiasing, false); - - QStyle *style = option.widget ? option.widget->style() : QApplication::style(); - - style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); - - painter->setRenderHint(QPainter::Antialiasing); -} - -static QSize viewItemTextSize(const QStyleOptionViewItemV4 *option) -{ - QStyle *style = option->widget ? option->widget->style() : QApplication::style(); - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(option->font); - textLayout.setText(option->text); - const int textMargin = - style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1; - QRect bounds(0, 0, 100 - 2 * textMargin, 600); - qreal height = 0, widthUsed = 0; - viewItemTextLayout(textLayout, bounds.width(), height, widthUsed); - const QSize size(qCeil(widthUsed), qCeil(height)); - return QSize(size.width() + 2 * textMargin, size.height()); -} - -void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - painter->save(); - painter->setClipRect(opt.rect); - - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - - // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); - const int iconSize = 48; - QRect iconbox = opt.rect; - const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; - QRect textRect = opt.rect; - QRect textHighlightRect = textRect; - // clip the decoration on top, remove width padding - textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0); - - textHighlightRect.adjust(0, iconSize + 5, 0, 0); - - // draw background - { - // FIXME: unused - // QSize textSize = viewItemTextSize ( &opt ); - QPalette::ColorGroup cg; - QStyleOptionViewItemV4 opt2(opt); - - if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) - { - if (!(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - else - cg = QPalette::Normal; - } - else - { - cg = QPalette::Disabled; - } - opt2.palette.setCurrentColorGroup(cg); - - // fill in background, if any - if (opt.backgroundBrush.style() != Qt::NoBrush) - { - QPointF oldBO = painter->brushOrigin(); - painter->setBrushOrigin(opt.rect.topLeft()); - painter->fillRect(opt.rect, opt.backgroundBrush); - painter->setBrushOrigin(oldBO); - } - - if (opt.showDecorationSelected) - { - drawSelectionRect(painter, opt2, opt.rect); - drawFocusRect(painter, opt2, opt.rect); - // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); - } - else - { - - // if ( opt.state & QStyle::State_Selected ) - { - // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, - // opt.widget ); - // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, - // QPalette::Highlight ) ); - drawSelectionRect(painter, opt2, textHighlightRect); - drawFocusRect(painter, opt2, textHighlightRect); - } - } - } - - // draw the icon - { - QIcon::Mode mode = QIcon::Normal; - if (!(opt.state & QStyle::State_Enabled)) - mode = QIcon::Disabled; - else if (opt.state & QStyle::State_Selected) - mode = QIcon::Selected; - QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; - - iconbox.setHeight(iconSize); - opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); - } - // set the text colors - QPalette::ColorGroup cg = - opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - if (opt.state & QStyle::State_Selected) - { - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - } - else - { - painter->setPen(opt.palette.color(cg, QPalette::Text)); - } - - // draw the text - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - textOption.setTextDirection(opt.direction); - textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment)); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(opt.font); - textLayout.setText(opt.text); - - qreal width, height; - viewItemTextLayout(textLayout, textRect.width(), height, width); - - const int lineCount = textLayout.lineCount(); - - const QRect layoutRect = QStyle::alignedRect( - opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); - const QPointF position = layoutRect.topLeft(); - for (int i = 0; i < lineCount; ++i) - { - const QTextLine line = textLayout.lineAt(i); - line.draw(painter, position); - } - - painter->restore(); -} - -QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItemV4 opt = option; - initStyleOption(&opt, index); - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - const int textMargin = - style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; - int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables - QSize szz = viewItemTextSize(&opt); - height += szz.height(); - // FIXME: maybe the icon items could scale and keep proportions? - QSize sz(100, height); - return sz; -} diff --git a/gui/widgets/InstanceDelegate.h b/gui/widgets/InstanceDelegate.h deleted file mode 100644 index 6f924405..00000000 --- a/gui/widgets/InstanceDelegate.h +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -class ListViewDelegate : public QStyledItemDelegate -{ -public: - explicit ListViewDelegate ( QObject* parent = 0 ); -protected: - void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; - QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; -}; diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index 0d4eab95..935f9fd3 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -33,6 +33,7 @@ #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logger/QsLog.h" +#include const static int GROUP_FILE_FORMAT_VERSION = 1; @@ -96,8 +97,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const return MMC->icons()->getIcon(key); } // for now. - case KCategorizedSortFilterProxyModel::CategorySortRole: - case KCategorizedSortFilterProxyModel::CategoryDisplayRole: + case GroupViewRoles::GroupRole: { return pdata->group(); } @@ -585,10 +585,8 @@ void InstanceList::propertiesChanged(BaseInstance *inst) } InstanceProxyModel::InstanceProxyModel(QObject *parent) - : KCategorizedSortFilterProxyModel(parent) + : GroupedProxyModel(parent) { - // disable since by default we are globally sorting by date: - setCategorizedModel(true); } bool InstanceProxyModel::subSortLessThan(const QModelIndex &left, diff --git a/logic/lists/InstanceList.h b/logic/lists/InstanceList.h index 0ce808e5..cda51a03 100644 --- a/logic/lists/InstanceList.h +++ b/logic/lists/InstanceList.h @@ -18,7 +18,7 @@ #include #include #include -#include "categorizedsortfilterproxymodel.h" +#include #include #include "logic/BaseInstance.h" @@ -129,7 +129,7 @@ protected: QSet m_groups; }; -class InstanceProxyModel : public KCategorizedSortFilterProxyModel +class InstanceProxyModel : public GroupedProxyModel { public: explicit InstanceProxyModel(QObject *parent = 0); -- cgit v1.2.3