From 8777b809cd33c48a5fff9697b56af2e2f028964b Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Fri, 19 Jan 2024 23:48:49 +0100 Subject: [PATCH 1/5] Sketcher: Draw proper checkbox control in Elements UI This ensures that checkboxes drawn in the Elements UI of the sketcher can be styled using the correct style. Three changes are introduced: 1. paint() method uses style provided by the widget, not application. 2. Indicator is drawn using PE_IndicatorCheckBox not CE_CheckBox - it allows to use ::indicator in qss to style it properly. This works the same as TreeView. 3. Fix for minor issues with text alignement and extension of selected items background to cover checkbox. This is how other controls of this kind work. --- src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 249d05dd88..c9d45ef713 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -128,7 +128,6 @@ public: const int border = 1; // 1px, looks good around buttons. const int leftMargin = 4;// 4px on the left of icons, looks good. mutable int customIconsMargin = 4; - const int textBottomMargin = 5;// 5px center the text. Q_SIGNALS: void itemHovered(QModelIndex); @@ -773,10 +772,11 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o ElementItem* item = getElementtItem(index); if (item) { + auto style = option.widget ? option.widget->style() : QApplication::style(); QStyleOptionButton checkboxstyle; - checkboxstyle.rect = option.rect; + checkboxstyle.rect = option.rect; checkboxstyle.state |= QStyle::State_Enabled; if (item->isVisible()) @@ -784,12 +784,17 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o else checkboxstyle.state |= QStyle::State_Off; - QRect checkboxrect = - QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle); + QRect checkboxrect + = style->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle, option.widget); - customIconsMargin = leftMargin + checkboxrect.width(); + checkboxstyle.rect = { + leftMargin, + option.rect.top() + (option.rect.height() - checkboxrect.height()) / 2, // vertically center the checkbox + checkboxrect.width(), + checkboxrect.height() + }; - QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkboxstyle, painter); + customIconsMargin = leftMargin + checkboxrect.width() + leftMargin; int height = option.rect.height(); int width = height;// icons are square. @@ -805,9 +810,9 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o painter->fillRect(rect, option.palette.base()); }; - QRect selection = QRect(customIconsMargin, + QRect selection = QRect(option.rect.x(), option.rect.y(), - option.rect.width() - customIconsMargin, + option.rect.width(), option.rect.height()); painter->fillRect(selection, option.palette.highlight());// paint the item as selected @@ -836,14 +841,20 @@ void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& o ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::mid, item->State); // getIcon(item->GeometryType); + style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkboxstyle, painter, option.widget); + painter->drawPixmap(x0 + border, btny, iconEdge.pixmap(iconsize, iconsize)); painter->drawPixmap(x0 + border + width, btny, iconStart.pixmap(iconsize, iconsize)); painter->drawPixmap(x0 + border + width * 2, btny, iconEnd.pixmap(iconsize, iconsize)); painter->drawPixmap(x0 + border + width * 3, btny, iconMid.pixmap(iconsize, iconsize)); // Label : + auto labelBoundingBox = painter->fontMetrics().tightBoundingRect(item->label); painter->drawText( - x0 + width * 4 + 3 * border, option.rect.y() + height - textBottomMargin, item->label); + x0 + width * 4 + 3 * border, + option.rect.bottom() - (option.rect.height() - labelBoundingBox.height()) / 2, + item->label + ); } } From 899d0d91302140676d57e68f358aa0aade26fa0c Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 20 Jan 2024 00:06:03 +0100 Subject: [PATCH 2/5] Sketcher: Make Constraints UI style aware This adds reference to the actual widget to the painting of Constraints List control of Sketcher. Thanks to this this list can now be styled using standard mechanisms of Qt framework like QSS. --- src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp index b927ec8ac3..363aea9859 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherConstraints.cpp @@ -262,12 +262,12 @@ public: static QIcon snell_driven( Gui::BitmapFactory().iconFromTheme("Constraint_SnellsLaw_Driven")); - auto selicon = [](const Sketcher::Constraint* constr, + auto selicon = [this](const Sketcher::Constraint* constr, const QIcon& normal, const QIcon& driven) -> QIcon { if (!constr->isActive) { QIcon darkIcon; - int w = QApplication::style()->pixelMetric(QStyle::PM_ListViewIconSize); + int w = listWidget()->style()->pixelMetric(QStyle::PM_ListViewIconSize); darkIcon.addPixmap(normal.pixmap(w, w, QIcon::Disabled, QIcon::Off), QIcon::Normal, QIcon::Off); @@ -466,7 +466,7 @@ protected: QStyleOptionViewItem options = option; initStyleOption(&options, index); - options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); + options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, option.widget); ConstraintItem* item = dynamic_cast(view->item(index.row())); if (!item || item->sketch->Constraints.getSize() <= item->ConstraintNbr) From f52c0d12f0aaa95107e3d825e1f184f05da8d793 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sat, 20 Jan 2024 16:55:38 +0100 Subject: [PATCH 3/5] Sketcher: Make Elements UI style aware This commit like previous one introduces ability to style list of elements in the sketcher. It also completely refactors the rendering delegate introducing dedicated method for calculating rects of each element and method for rendering each sub control. Thanks to this refactor we can be sure that areas drawn are the same as areas checked for events in editorEvent. --- src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 326 ++++++++++++------ 1 file changed, 213 insertions(+), 113 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index c9d45ef713..16b0b25e6c 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -115,25 +115,37 @@ class ElementItemDelegate: public QStyledItemDelegate { Q_OBJECT public: + /// Enum containing all controls rendered in this item. Controls in that enum MUST be in order. + enum SubControl : int { + CheckBox, + LineSelect, + StartSelect, + EndSelect, + MidSelect, + Label + }; + explicit ElementItemDelegate(ElementView* parent); - ~ElementItemDelegate() override; + ~ElementItemDelegate() override = default; void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override; - ElementItem* getElementtItem(const QModelIndex& index) const; + ElementItem* getElementItem(const QModelIndex& index) const; - const int border = 1; // 1px, looks good around buttons. - const int leftMargin = 4;// 4px on the left of icons, looks good. - mutable int customIconsMargin = 4; + QRect subControlRect(SubControl element, const QStyleOptionViewItem& option, const QModelIndex& index) const; + void drawSubControl(SubControl element, QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; + + const int gap = 4; // 4px of spacing between consecutive elements Q_SIGNALS: void itemHovered(QModelIndex); void itemChecked(QModelIndex, Qt::CheckState state); }; +// clang-format on // helper class to store additional information about the listWidget entry. class ElementItem: public QListWidgetItem { @@ -153,8 +165,13 @@ public: Hidden = 2, }; - ElementItem(int elementnr, int startingVertex, int midVertex, int endVertex, - Base::Type geometryType, GeometryState state, const QString& lab, + ElementItem(int elementnr, + int startingVertex, + int midVertex, + int endVertex, + Base::Type geometryType, + GeometryState state, + const QString& lab, ViewProviderSketch* sketchView) : ElementNbr(elementnr) , StartingVertex(startingVertex) @@ -178,7 +195,6 @@ public: bool isVisible() { - if (State != GeometryState::External) { const auto geo = sketchView->getSketchObject()->getGeometry(ElementNbr); if (geo) { @@ -194,6 +210,35 @@ public: return true; } + QVariant data(int role) const override + { + // In order for content-box to include size of the 4 geometry icons we need to provide + // Qt with information about decoration (icon) size. This is hack to work around Qt + // limitation of not knowing about padding, border and margin boxes of stylesheets + // thus being unable to provide proper sizeHint for stylesheets to render correctly + if (role == Qt::DecorationRole) { + int size = listWidget()->style()->pixelMetric(QStyle::PM_ListViewIconSize); + + return QIcon(QPixmap(QSize(size, size))); + } + + return QListWidgetItem::data(role); + } + + bool isGeometrySelected(Sketcher::PointPos pos) const + { + switch (pos) { + case Sketcher::PointPos::none: + return isLineSelected; + case Sketcher::PointPos::start: + return isStartingPointSelected; + case Sketcher::PointPos::end: + return isEndPointSelected; + case Sketcher::PointPos::mid: + return isMidPointSelected; + } + } + int ElementNbr; int StartingVertex; int MidVertex; @@ -217,6 +262,7 @@ public: private: ViewProviderSketch* sketchView; }; +// clang-format off class ElementFilterList: public QListWidget { @@ -497,6 +543,7 @@ ElementView::~ElementView() void ElementView::changeLayer(int layer) { App::Document* doc = App::GetApplication().getActiveDocument(); + if (!doc) return; @@ -507,7 +554,6 @@ void ElementView::changeLayer(int layer) auto geoids = getGeoIdsOfEdgesFromNames(sketchobject, ft->getSubNames()); - auto geometry = sketchobject->Geometry.getValues(); auto newgeometry(geometry); @@ -737,6 +783,8 @@ void ElementView::deleteSelectedItems() void ElementView::onIndexHovered(QModelIndex index) { + update(index); + Q_EMIT onItemHovered(itemFromIndex(index)); } @@ -755,166 +803,218 @@ ElementItem* ElementView::itemFromIndex(const QModelIndex& index) return static_cast(QListWidget::itemFromIndex(index)); } +// clang-format on /* ElementItem delegate ---------------------------------------------------- */ - ElementItemDelegate::ElementItemDelegate(ElementView* parent) : QStyledItemDelegate(parent) -{// This class relies on the parent being an ElementView, see getElementtItem +{ // This class relies on the parent being an ElementView, see getElementtItem } -ElementItemDelegate::~ElementItemDelegate() -{} - - -void ElementItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, +void ElementItemDelegate::paint(QPainter* painter, + const QStyleOptionViewItem& option, const QModelIndex& index) const { - ElementItem* item = getElementtItem(index); + ElementItem* item = getElementItem(index); - if (item) { - auto style = option.widget ? option.widget->style() : QApplication::style(); + if (!item) { + return; + } - QStyleOptionButton checkboxstyle; + auto style = option.widget ? option.widget->style() : QApplication::style(); - checkboxstyle.rect = option.rect; - checkboxstyle.state |= QStyle::State_Enabled; + QStyleOptionViewItem itemOption = option; - if (item->isVisible()) - checkboxstyle.state |= QStyle::State_On; - else - checkboxstyle.state |= QStyle::State_Off; + initStyleOption(&itemOption, index); - QRect checkboxrect - = style->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle, option.widget); + if (item->isLineSelected || item->isStartingPointSelected || item->isEndPointSelected + || item->isMidPointSelected) { + itemOption.state |= QStyle::State_Active; + } - checkboxstyle.rect = { - leftMargin, - option.rect.top() + (option.rect.height() - checkboxrect.height()) / 2, // vertically center the checkbox - checkboxrect.width(), - checkboxrect.height() - }; + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &itemOption, painter, option.widget); - customIconsMargin = leftMargin + checkboxrect.width() + leftMargin; + drawSubControl(SubControl::CheckBox, painter, option, index); + drawSubControl(SubControl::LineSelect, painter, option, index); + drawSubControl(SubControl::StartSelect, painter, option, index); + drawSubControl(SubControl::EndSelect, painter, option, index); + drawSubControl(SubControl::MidSelect, painter, option, index); + drawSubControl(SubControl::Label, painter, option, index); +} - int height = option.rect.height(); - int width = height;// icons are square. - int x0 = option.rect.x() + customIconsMargin; - int iconsize = height - 2 * border; - int btny = option.rect.y() + border; +QRect ElementItemDelegate::subControlRect(SubControl element, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + auto itemOption = option; - if (item->isLineSelected || item->isStartingPointSelected || item->isEndPointSelected - || item->isMidPointSelected) {// option.state & QStyle::State_Selected + auto style = option.widget ? option.widget->style() : QApplication::style(); - auto unselecticon = [&](int iconnumber) { - QRect rect {x0 + border + width * iconnumber, btny, iconsize, iconsize}; - painter->fillRect(rect, option.palette.base()); - }; + initStyleOption(&itemOption, index); - QRect selection = QRect(option.rect.x(), - option.rect.y(), - option.rect.width(), - option.rect.height()); + QRect checkBoxRect = + style->subElementRect(QStyle::SE_CheckBoxIndicator, &itemOption, option.widget); - painter->fillRect(selection, option.palette.highlight());// paint the item as selected + checkBoxRect.moveTo(gap, + option.rect.top() + (option.rect.height() - checkBoxRect.height()) / 2); - // Repaint individual icons - if (!item->isLineSelected) - unselecticon(0); + if (element == SubControl::CheckBox) { + return checkBoxRect; + } - if (!item->isStartingPointSelected) - unselecticon(1); + QRect selectRect = + style->subElementRect(QStyle::SE_ItemViewItemDecoration, &itemOption, option.widget) + .translated(checkBoxRect.right() + gap, 0); - if (!item->isEndPointSelected) - unselecticon(2); + unsigned pos = element - SubControl::LineSelect; - if (!item->isMidPointSelected) - unselecticon(3); + auto rect = selectRect.translated((selectRect.width() + gap) * pos, 0); + + if (element != SubControl::Label) { + return rect; + } + + rect.setRight(itemOption.rect.right()); + + return rect; +} + +void ElementItemDelegate::drawSubControl(SubControl element, + QPainter* painter, + const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + auto item = getElementItem(index); + auto style = option.widget ? option.widget->style() : QApplication::style(); + + auto rect = subControlRect(element, option, index); + + auto mousePos = option.widget->mapFromGlobal(QCursor::pos()); + auto isHovered = rect.contains(mousePos); + + auto drawSelectIcon = [&](Sketcher::PointPos pos) { + auto icon = ElementWidgetIcons::getIcon(item->GeometryType, pos, item->State); + auto opacity = 0.4f; + + if (isHovered) { + opacity = 0.8f; } - auto& iconEdge = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::none, item->State); - auto& iconStart = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::start, item->State); - auto& iconEnd = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::end, item->State); - auto& iconMid = - ElementWidgetIcons::getIcon(item->GeometryType, Sketcher::PointPos::mid, item->State); - // getIcon(item->GeometryType); + if (item->isGeometrySelected(pos)) { + opacity = 1.0f; + } - style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkboxstyle, painter, option.widget); + painter->setOpacity(opacity); + painter->drawPixmap(rect, icon.pixmap(rect.size())); + }; - painter->drawPixmap(x0 + border, btny, iconEdge.pixmap(iconsize, iconsize)); - painter->drawPixmap(x0 + border + width, btny, iconStart.pixmap(iconsize, iconsize)); - painter->drawPixmap(x0 + border + width * 2, btny, iconEnd.pixmap(iconsize, iconsize)); - painter->drawPixmap(x0 + border + width * 3, btny, iconMid.pixmap(iconsize, iconsize)); + painter->save(); - // Label : - auto labelBoundingBox = painter->fontMetrics().tightBoundingRect(item->label); - painter->drawText( - x0 + width * 4 + 3 * border, - option.rect.bottom() - (option.rect.height() - labelBoundingBox.height()) / 2, - item->label - ); + switch (element) { + case SubControl::CheckBox: { + QStyleOptionButton checkboxOption; + + checkboxOption.initFrom(option.widget); + + checkboxOption.state |= QStyle::State_Enabled; + checkboxOption.rect = rect; + + if (item->isVisible()) { + checkboxOption.state |= QStyle::State_On; + } + else { + checkboxOption.state |= QStyle::State_Off; + } + + style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, + &checkboxOption, + painter, + option.widget); + + break; + } + + case LineSelect: { + drawSelectIcon(Sketcher::PointPos::none); + break; + } + + case StartSelect: { + drawSelectIcon(Sketcher::PointPos::start); + break; + } + + case EndSelect: { + drawSelectIcon(Sketcher::PointPos::end); + break; + } + + case MidSelect: { + drawSelectIcon(Sketcher::PointPos::mid); + break; + } + + case Label: { + QRect rect = subControlRect(SubControl::Label, option, index); + + auto labelBoundingBox = painter->fontMetrics().tightBoundingRect(item->label); + + painter->drawText(rect.x(), + option.rect.bottom() + - (option.rect.height() - labelBoundingBox.height()) / 2, + item->label); + + break; + } } + + painter->restore(); } +// clang-format off bool ElementItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { - auto getSubElementType = [&](ElementItem* item, int xPos, int width) { - bool label = (xPos > option.rect.x() + customIconsMargin + width * 4 + border); + auto item = getElementItem(index); - if ((xPos < option.rect.x() + customIconsMargin + width + border) - || (item->GeometryType != Part::GeomPoint::getClassTypeId() && label)) + auto getSubElementType = [&](QPoint pos) { + if (subControlRect(SubControl::LineSelect, option, index).contains(pos)) { return SubElementType::edge; - if (xPos < option.rect.x() + customIconsMargin + width * 2 + border - || (item->GeometryType == Part::GeomPoint::getClassTypeId() && label)) + } else if (subControlRect(SubControl::StartSelect, option, index).contains(pos)) { return SubElementType::start; - if (xPos < option.rect.x() + customIconsMargin + width * 3 + border) + } else if (subControlRect(SubControl::EndSelect, option, index).contains(pos)) { return SubElementType::end; - else if (xPos < option.rect.x() + customIconsMargin + width * 4 + border) + } else if (subControlRect(SubControl::MidSelect, option, index).contains(pos)) { return SubElementType::mid; - else - return SubElementType::none; + } else { + // depending on geometry type by default we select either point or edge + return item->GeometryType == Part::GeomPoint::getClassTypeId() ? SubElementType::start : SubElementType::edge; + } }; if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) { - QMouseEvent* mEvent = static_cast(event); - ElementItem* item = getElementtItem(index); + auto mouseEvent = static_cast(event); - int xPos = mEvent->pos().x(); - int width = option.rect.height();// icons are square + item->clickedOn = getSubElementType(mouseEvent->pos()); + item->rightClicked = mouseEvent->button() == Qt::RightButton; - item->clickedOn = getSubElementType(item, xPos, width); + QRect checkboxRect = subControlRect(SubControl::CheckBox, option, index); - if (mEvent->button() == Qt::RightButton) - item->rightClicked = true; - - QRect checkboxrect = QRect( - leftMargin, option.rect.y(), customIconsMargin - leftMargin, option.rect.height()); - - if (mEvent->button() == Qt::LeftButton && checkboxrect.contains(mEvent->pos())) { + if (mouseEvent->button() == Qt::LeftButton && checkboxRect.contains(mouseEvent->pos())) { Q_EMIT itemChecked(index, item->isVisible() ? Qt::Unchecked : Qt::Checked); } } else if (event->type() == QEvent::MouseMove) { - SubElementType typeUnderMouse; - QMouseEvent* mEvent = static_cast(event); - int xPos = mEvent->pos().x(); - int width = option.rect.height();// icons are square + auto mouseEvent = static_cast(event); - ElementItem* item = getElementtItem(index); + item->hovered = getSubElementType(mouseEvent->pos()); - typeUnderMouse = getSubElementType(item, xPos, width); - - item->hovered = typeUnderMouse; Q_EMIT itemHovered(index); } return QStyledItemDelegate::editorEvent(event, model, option, index); } - -ElementItem* ElementItemDelegate::getElementtItem(const QModelIndex& index) const +const QWidget *w +ElementItem* ElementItemDelegate::getElementItem(const QModelIndex& index) const { ElementView* elementView = static_cast(parent()); return elementView->itemFromIndex(index); From 8c2310eaf350986bff097c669bf19f612c838b22 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 21 Jan 2024 00:13:35 +0100 Subject: [PATCH 4/5] Sketcher: Fix checkbox behavior for Element checkboxes This creates new method overload for changing layer of exact ElementItem. This fixes strange behavior of the checkboxes for elements that were dependent not on what user clicked but on what is selected. Fixes: #8814 --- src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 57 ++++++++++++++++--- src/Mod/Sketcher/Gui/TaskSketcherElements.h | 1 + 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 16b0b25e6c..9949d16b2e 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -239,6 +239,11 @@ public: } } + Sketcher::SketchObject* getSketchObject() const + { + return sketchView->getSketchObject(); + } + int ElementNbr; int StartingVertex; int MidVertex; @@ -586,6 +591,47 @@ void ElementView::changeLayer(int layer) doc->commitTransaction(); } +void ElementView::changeLayer(ElementItem* item, int layer) +{ + App::Document* doc = App::GetApplication().getActiveDocument(); + + if (!doc) { + return; + } + + doc->openTransaction("Geometry Layer Change"); + + auto sketchObject = item->getSketchObject(); + + auto geometry = sketchObject->Geometry.getValues(); + auto newGeometry(geometry); + + auto geoid = item->ElementNbr; + + // currently only internal geometry can be changed from one layer to another + if (geoid >= 0) { + auto currentLayer = getSafeGeomLayerId(geometry[geoid]); + + if (currentLayer != layer) { + auto geo = geometry[geoid]->clone(); + setSafeGeomLayerId(geo, layer); + newGeometry[geoid] = geo; + + sketchObject->Geometry.setValues(std::move(newGeometry)); + sketchObject->solve(); + } + } + else { + Gui::TranslatedUserWarning( + sketchObject, + QObject::tr("Unsupported visual layer operation"), + QObject::tr("It is currently unsupported to move external geometry to another " + "visual layer. External geometry will be omitted")); + } + + doc->commitTransaction(); +} + void ElementView::contextMenuEvent(QContextMenuEvent* event) { QMenu menu; @@ -788,14 +834,11 @@ void ElementView::onIndexHovered(QModelIndex index) Q_EMIT onItemHovered(itemFromIndex(index)); } -void ElementView::onIndexChecked(QModelIndex, Qt::CheckState state) +void ElementView::onIndexChecked(QModelIndex index, Qt::CheckState state) { - if (state == Qt::Checked) { - changeLayer(static_cast(ElementItem::Layer::Default)); - } - else { - changeLayer(static_cast(ElementItem::Layer::Hidden)); - } + auto item = itemFromIndex(index); + + changeLayer(item, static_cast(state == Qt::Checked ? ElementItem::Layer::Default : ElementItem::Layer::Hidden)); } ElementItem* ElementView::itemFromIndex(const QModelIndex& index) diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.h b/src/Mod/Sketcher/Gui/TaskSketcherElements.h index 1284a804d2..6965b44ea1 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.h +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.h @@ -109,6 +109,7 @@ Q_SIGNALS: private: void changeLayer(int layer); + void changeLayer(ElementItem* item, int layer); }; class ElementFilterList; From 993248b4dba710b9b1432f974b03bb6baa3fcda2 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 21 Jan 2024 19:47:42 +0100 Subject: [PATCH 5/5] Sketcher: Disable visibility checkbox for external geometry --- .../images_dark-light/check_dark_disabled.svg | 51 +++++++++++++++++++ .../check_light_disabled.svg | 51 +++++++++++++++++++ src/Mod/Sketcher/Gui/TaskSketcherElements.cpp | 25 ++++++--- 3 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg create mode 100644 src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg diff --git a/src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg b/src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg new file mode 100644 index 0000000000..5355029ad4 --- /dev/null +++ b/src/Gui/Stylesheets/images_dark-light/check_dark_disabled.svg @@ -0,0 +1,51 @@ + + + + + + + + + image/svg+xml + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + + diff --git a/src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg b/src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg new file mode 100644 index 0000000000..71445d475f --- /dev/null +++ b/src/Gui/Stylesheets/images_dark-light/check_light_disabled.svg @@ -0,0 +1,51 @@ + + + + + + + + + image/svg+xml + + + + Pablo Gil + + + + + SVG + template + + + + + + + + + + diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp index 9949d16b2e..41835ede72 100644 --- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp +++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp @@ -193,7 +193,12 @@ public: ~ElementItem() override {} - bool isVisible() + bool canBeHidden() const + { + return State != GeometryState::External; + } + + bool isVisible() const { if (State != GeometryState::External) { const auto geo = sketchView->getSketchObject()->getGeometry(ElementNbr); @@ -957,10 +962,14 @@ void ElementItemDelegate::drawSubControl(SubControl element, QStyleOptionButton checkboxOption; checkboxOption.initFrom(option.widget); - - checkboxOption.state |= QStyle::State_Enabled; checkboxOption.rect = rect; + checkboxOption.state.setFlag(QStyle::State_Enabled, item->canBeHidden()); + + if (isHovered) { + checkboxOption.state |= QStyle::State_MouseOver; + } + if (item->isVisible()) { checkboxOption.state |= QStyle::State_On; } @@ -1040,10 +1049,12 @@ bool ElementItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, item->clickedOn = getSubElementType(mouseEvent->pos()); item->rightClicked = mouseEvent->button() == Qt::RightButton; - QRect checkboxRect = subControlRect(SubControl::CheckBox, option, index); + if (item->canBeHidden()) { + QRect checkboxRect = subControlRect(SubControl::CheckBox, option, index); - if (mouseEvent->button() == Qt::LeftButton && checkboxRect.contains(mouseEvent->pos())) { - Q_EMIT itemChecked(index, item->isVisible() ? Qt::Unchecked : Qt::Checked); + if (mouseEvent->button() == Qt::LeftButton && checkboxRect.contains(mouseEvent->pos())) { + Q_EMIT itemChecked(index, item->isVisible() ? Qt::Unchecked : Qt::Checked); + } } } else if (event->type() == QEvent::MouseMove) { @@ -1056,7 +1067,7 @@ bool ElementItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, return QStyledItemDelegate::editorEvent(event, model, option, index); } -const QWidget *w + ElementItem* ElementItemDelegate::getElementItem(const QModelIndex& index) const { ElementView* elementView = static_cast(parent());