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 @@
+
+
+
+
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 @@
+
+
+
+
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)
diff --git a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp
index 249d05dd88..41835ede72 100644
--- a/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp
+++ b/src/Mod/Sketcher/Gui/TaskSketcherElements.cpp
@@ -115,26 +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;
- const int textBottomMargin = 5;// 5px center the text.
+ 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
{
@@ -154,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)
@@ -177,9 +193,13 @@ 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);
if (geo) {
@@ -195,6 +215,40 @@ 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;
+ }
+ }
+
+ Sketcher::SketchObject* getSketchObject() const
+ {
+ return sketchView->getSketchObject();
+ }
+
int ElementNbr;
int StartingVertex;
int MidVertex;
@@ -218,6 +272,7 @@ public:
private:
ViewProviderSketch* sketchView;
};
+// clang-format off
class ElementFilterList: public QListWidget
{
@@ -498,6 +553,7 @@ ElementView::~ElementView()
void ElementView::changeLayer(int layer)
{
App::Document* doc = App::GetApplication().getActiveDocument();
+
if (!doc)
return;
@@ -508,7 +564,6 @@ void ElementView::changeLayer(int layer)
auto geoids = getGeoIdsOfEdgesFromNames(sketchobject, ft->getSubNames());
-
auto geometry = sketchobject->Geometry.getValues();
auto newgeometry(geometry);
@@ -541,6 +596,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;
@@ -738,17 +834,16 @@ void ElementView::deleteSelectedItems()
void ElementView::onIndexHovered(QModelIndex index)
{
+ update(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)
@@ -756,154 +851,224 @@ 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) {
+ if (!item) {
+ return;
+ }
- QStyleOptionButton checkboxstyle;
- checkboxstyle.rect = option.rect;
+ auto style = option.widget ? option.widget->style() : QApplication::style();
- 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 =
- QApplication::style()->subElementRect(QStyle::SE_CheckBoxIndicator, &checkboxstyle);
+ if (item->isLineSelected || item->isStartingPointSelected || item->isEndPointSelected
+ || item->isMidPointSelected) {
+ itemOption.state |= QStyle::State_Active;
+ }
- customIconsMargin = leftMargin + checkboxrect.width();
+ style->drawPrimitive(QStyle::PE_PanelItemViewItem, &itemOption, painter, option.widget);
- QApplication::style()->drawControl(QStyle::CE_CheckBox, &checkboxstyle, painter);
+ 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(customIconsMargin,
- option.rect.y(),
- option.rect.width() - customIconsMargin,
- 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;
+ }
- 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->setOpacity(opacity);
+ painter->drawPixmap(rect, icon.pixmap(rect.size()));
+ };
- // Label :
- painter->drawText(
- x0 + width * 4 + 3 * border, option.rect.y() + height - textBottomMargin, item->label);
+ painter->save();
+
+ switch (element) {
+ case SubControl::CheckBox: {
+ QStyleOptionButton checkboxOption;
+
+ checkboxOption.initFrom(option.widget);
+ 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;
+ }
+ 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);
+ if (item->canBeHidden()) {
+ 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())) {
- 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) {
- 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
+ElementItem* ElementItemDelegate::getElementItem(const QModelIndex& index) const
{
ElementView* elementView = static_cast(parent());
return elementView->itemFromIndex(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;