mirror of
https://github.com/leozide/leocad.git
synced 2026-10-26 22:10:32 +00:00
Keep part names visible and highlight selected rows
Freeze the Part column with a width cap and full-name tooltips. Synchronize row selection across views, forward wheel scrolling, and avoid the initial cell highlight. Closes #565
This commit is contained in:
1 parent
6d8f778e3d
commit
50aa42f195
3 files changed
+236
-2
No files matched your search
@@ -6,6 +6,202 @@
|
||||
#include "lc_application.h"
|
||||
#include "pieceinf.h"
|
||||
|
||||
class lcFrozenPartsView : public QTableView
|
||||
{
|
||||
public:
|
||||
explicit lcFrozenPartsView(lcPartsTableWidget* PartsTable)
|
||||
: QTableView(PartsTable), mPartsTable(PartsTable)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& Index, const QEvent* Event = nullptr) const override
|
||||
{
|
||||
return mPartsTable->TrackSelectionCommand(Index, Event, QTableView::selectionCommand(Index, Event));
|
||||
}
|
||||
|
||||
void setSelection(const QRect& Rect, QItemSelectionModel::SelectionFlags Command) override
|
||||
{
|
||||
if (!mPartsTable->SelectSharedRange(Command))
|
||||
QTableView::setSelection(Rect, Command);
|
||||
}
|
||||
|
||||
private:
|
||||
lcPartsTableWidget* mPartsTable;
|
||||
};
|
||||
|
||||
lcPartsTableWidget::lcPartsTableWidget(QWidget* Parent)
|
||||
: QTableWidget(Parent)
|
||||
{
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::FreezePartColumn()
|
||||
{
|
||||
mPartColumnWidth = qMax(sizeHintForColumn(0), horizontalHeader()->sectionSizeHint(0));
|
||||
horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
|
||||
setTextElideMode(Qt::ElideRight);
|
||||
for (int Row = 0; Row < rowCount(); Row++)
|
||||
if (QTableWidgetItem* Item = item(Row, 0))
|
||||
Item->setToolTip(Item->text());
|
||||
|
||||
// Both views share the data and selection, so sorting and row highlights stay in sync.
|
||||
mFrozenColumn = new lcFrozenPartsView(this);
|
||||
mFrozenColumn->setModel(model());
|
||||
mFrozenColumn->setSelectionModel(selectionModel());
|
||||
mFrozenColumn->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
mFrozenColumn->setSelectionMode(selectionMode());
|
||||
mFrozenColumn->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
mFrozenColumn->setTextElideMode(Qt::ElideRight);
|
||||
mFrozenColumn->setFocusPolicy(Qt::NoFocus);
|
||||
mFrozenColumn->setFocusProxy(this);
|
||||
mFrozenColumn->setFrameShape(QFrame::NoFrame);
|
||||
mFrozenColumn->verticalHeader()->hide();
|
||||
mFrozenColumn->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
|
||||
mFrozenColumn->horizontalHeader()->setSectionsClickable(true);
|
||||
mFrozenColumn->horizontalHeader()->setSortIndicatorShown(true);
|
||||
mFrozenColumn->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
mFrozenColumn->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
mFrozenColumn->viewport()->installEventFilter(this);
|
||||
mFrozenColumn->horizontalHeader()->viewport()->installEventFilter(this);
|
||||
setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
mFrozenColumn->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
|
||||
for (int Column = 1; Column < columnCount(); Column++)
|
||||
mFrozenColumn->hideColumn(Column);
|
||||
|
||||
for (int Row = 0; Row < rowCount(); Row++)
|
||||
mFrozenColumn->setRowHeight(Row, rowHeight(Row));
|
||||
|
||||
connect(verticalScrollBar(), &QScrollBar::valueChanged, mFrozenColumn->verticalScrollBar(), &QScrollBar::setValue);
|
||||
connect(mFrozenColumn->verticalScrollBar(), &QScrollBar::valueChanged, verticalScrollBar(), &QScrollBar::setValue);
|
||||
connect(verticalHeader(), &QHeaderView::sectionResized, this, [this](int Row, int, int Height)
|
||||
{
|
||||
mFrozenColumn->setRowHeight(Row, Height);
|
||||
});
|
||||
connect(horizontalHeader(), &QHeaderView::sectionResized, this, [this](int Column, int, int)
|
||||
{
|
||||
if (Column == 0)
|
||||
UpdateFrozenGeometry();
|
||||
});
|
||||
connect(horizontalHeader(), &QHeaderView::sortIndicatorChanged, mFrozenColumn->horizontalHeader(), &QHeaderView::setSortIndicator);
|
||||
connect(mFrozenColumn->horizontalHeader(), &QHeaderView::sectionClicked, this, [this](int)
|
||||
{
|
||||
const Qt::SortOrder Order = horizontalHeader()->sortIndicatorSection() == 0 && horizontalHeader()->sortIndicatorOrder() == Qt::AscendingOrder
|
||||
? Qt::DescendingOrder : Qt::AscendingOrder;
|
||||
horizontalHeader()->setSortIndicator(0, Order);
|
||||
});
|
||||
mFrozenColumn->horizontalHeader()->setSortIndicator(horizontalHeader()->sortIndicatorSection(), horizontalHeader()->sortIndicatorOrder());
|
||||
|
||||
viewport()->stackUnder(mFrozenColumn);
|
||||
UpdateFrozenGeometry();
|
||||
mFrozenColumn->show();
|
||||
}
|
||||
|
||||
bool lcPartsTableWidget::eventFilter(QObject* Object, QEvent* Event)
|
||||
{
|
||||
if (mFrozenColumn && Event->type() == QEvent::Wheel &&
|
||||
(Object == mFrozenColumn->viewport() || Object == mFrozenColumn->horizontalHeader()->viewport()))
|
||||
{
|
||||
QCoreApplication::sendEvent(viewport(), Event);
|
||||
return true;
|
||||
}
|
||||
|
||||
return QTableWidget::eventFilter(Object, Event);
|
||||
}
|
||||
|
||||
QItemSelectionModel::SelectionFlags lcPartsTableWidget::TrackSelectionCommand(const QModelIndex& Index, const QEvent* Event, QItemSelectionModel::SelectionFlags Command) const
|
||||
{
|
||||
// Qt keeps a separate range anchor in each view, even with a shared selection model.
|
||||
mSelectionEnd = Index;
|
||||
if (Command.testFlag(QItemSelectionModel::Current))
|
||||
{
|
||||
if (!mSelectionAnchor.isValid())
|
||||
mSelectionAnchor = currentIndex().isValid() ? currentIndex() : Index;
|
||||
}
|
||||
else if (Command != QItemSelectionModel::NoUpdate || (Event && Event->type() == QEvent::MouseButtonPress))
|
||||
mSelectionAnchor = Index;
|
||||
|
||||
return Command;
|
||||
}
|
||||
|
||||
QItemSelectionModel::SelectionFlags lcPartsTableWidget::selectionCommand(const QModelIndex& Index, const QEvent* Event) const
|
||||
{
|
||||
return TrackSelectionCommand(Index, Event, QTableWidget::selectionCommand(Index, Event));
|
||||
}
|
||||
|
||||
bool lcPartsTableWidget::SelectSharedRange(QItemSelectionModel::SelectionFlags Command)
|
||||
{
|
||||
if (!Command.testFlag(QItemSelectionModel::Current) || !mSelectionAnchor.isValid() || !mSelectionEnd.isValid())
|
||||
return false;
|
||||
|
||||
const int FirstRow = qMin(mSelectionAnchor.row(), mSelectionEnd.row());
|
||||
const int LastRow = qMax(mSelectionAnchor.row(), mSelectionEnd.row());
|
||||
selectionModel()->select(QItemSelection(model()->index(FirstRow, 0), model()->index(LastRow, columnCount() - 1)), Command);
|
||||
return true;
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::setSelection(const QRect& Rect, QItemSelectionModel::SelectionFlags Command)
|
||||
{
|
||||
if (!SelectSharedRange(Command))
|
||||
QTableWidget::setSelection(Rect, Command);
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::UpdateFrozenGeometry()
|
||||
{
|
||||
if (!mFrozenColumn)
|
||||
return;
|
||||
|
||||
// Reserve at least half the viewport for counts, while letting short names use less space.
|
||||
const int PartWidth = qMin(mPartColumnWidth, qMax(1, viewport()->width() / 2));
|
||||
if (columnWidth(0) != PartWidth)
|
||||
setColumnWidth(0, PartWidth);
|
||||
|
||||
mFrozenColumn->setColumnWidth(0, columnWidth(0));
|
||||
mFrozenColumn->horizontalHeader()->setFixedHeight(horizontalHeader()->height());
|
||||
mFrozenColumn->setGeometry(viewport()->geometry().x(), frameWidth(), columnWidth(0), viewport()->height() + horizontalHeader()->height());
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::focusInEvent(QFocusEvent* Event)
|
||||
{
|
||||
const bool HasCurrent = currentIndex().isValid();
|
||||
QTableWidget::focusInEvent(Event);
|
||||
|
||||
// Receiving focus should not mark the first cell before the user chooses a row.
|
||||
if (!HasCurrent)
|
||||
selectionModel()->clearCurrentIndex();
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::resizeEvent(QResizeEvent* Event)
|
||||
{
|
||||
QTableWidget::resizeEvent(Event);
|
||||
UpdateFrozenGeometry();
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::EnsureCountVisible(const QModelIndex& Index)
|
||||
{
|
||||
if (mFrozenColumn && Index.column() > 0 && visualRect(Index).left() < columnWidth(0))
|
||||
horizontalScrollBar()->setValue(horizontalScrollBar()->value() + visualRect(Index).left() - columnWidth(0));
|
||||
}
|
||||
|
||||
QModelIndex lcPartsTableWidget::moveCursor(CursorAction Action, Qt::KeyboardModifiers Modifiers)
|
||||
{
|
||||
const QModelIndex Index = QTableWidget::moveCursor(Action, Modifiers);
|
||||
EnsureCountVisible(Index);
|
||||
return Index;
|
||||
}
|
||||
|
||||
void lcPartsTableWidget::scrollTo(const QModelIndex& Index, ScrollHint Hint)
|
||||
{
|
||||
if (!mFrozenColumn || Index.column() > 0)
|
||||
{
|
||||
QTableWidget::scrollTo(Index, Hint);
|
||||
EnsureCountVisible(Index);
|
||||
}
|
||||
else
|
||||
mFrozenColumn->scrollTo(Index, Hint);
|
||||
}
|
||||
|
||||
class lcPartsTableWidgetItem : public QTableWidgetItem
|
||||
{
|
||||
public:
|
||||
@@ -138,6 +334,8 @@ lcPropertiesDialog::lcPropertiesDialog(QWidget* Parent, lcPropertiesDialogOption
|
||||
Item->mLast = true;
|
||||
Item->setTextAlignment(Qt::AlignCenter);
|
||||
PartsTable->setItem((int)InfoTotals.size(), ColorCount + 1, Item);
|
||||
|
||||
ui->PartsTable->FreezePartColumn();
|
||||
}
|
||||
|
||||
lcPropertiesDialog::~lcPropertiesDialog()
|
||||
|
||||
@@ -14,6 +14,33 @@ namespace Ui
|
||||
class lcPropertiesDialog;
|
||||
}
|
||||
|
||||
class lcPartsTableWidget : public QTableWidget
|
||||
{
|
||||
public:
|
||||
explicit lcPartsTableWidget(QWidget* Parent = nullptr);
|
||||
void FreezePartColumn();
|
||||
void scrollTo(const QModelIndex& Index, ScrollHint Hint = EnsureVisible) override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* Object, QEvent* Event) override;
|
||||
void focusInEvent(QFocusEvent* Event) override;
|
||||
void resizeEvent(QResizeEvent* Event) override;
|
||||
QModelIndex moveCursor(CursorAction Action, Qt::KeyboardModifiers Modifiers) override;
|
||||
QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& Index, const QEvent* Event = nullptr) const override;
|
||||
void setSelection(const QRect& Rect, QItemSelectionModel::SelectionFlags Command) override;
|
||||
|
||||
private:
|
||||
friend class lcFrozenPartsView;
|
||||
QItemSelectionModel::SelectionFlags TrackSelectionCommand(const QModelIndex& Index, const QEvent* Event, QItemSelectionModel::SelectionFlags Command) const;
|
||||
bool SelectSharedRange(QItemSelectionModel::SelectionFlags Command);
|
||||
void UpdateFrozenGeometry();
|
||||
void EnsureCountVisible(const QModelIndex& Index);
|
||||
QTableView* mFrozenColumn = nullptr;
|
||||
int mPartColumnWidth = 0;
|
||||
mutable QPersistentModelIndex mSelectionAnchor;
|
||||
mutable QPersistentModelIndex mSelectionEnd;
|
||||
};
|
||||
|
||||
class lcPropertiesDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -30,4 +57,3 @@ public slots:
|
||||
private:
|
||||
Ui::lcPropertiesDialog* ui;
|
||||
};
|
||||
|
||||
@@ -92,13 +92,16 @@
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="PartsTable">
|
||||
<widget class="lcPartsTableWidget" name="PartsTable">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>75</number>
|
||||
</attribute>
|
||||
@@ -134,6 +137,13 @@
|
||||
<tabstop>CommentsEdit</tabstop>
|
||||
<tabstop>PartsTable</tabstop>
|
||||
</tabstops>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>lcPartsTableWidget</class>
|
||||
<extends>QTableWidget</extends>
|
||||
<header>lc_qpropertiesdialog.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
Reference in new issue
Block a user