Fix color sampling while multiple items are selected

This commit is contained in:
Rebecca Breu 2024-05-04 16:40:36 +02:00
parent 2af6a75cc3
commit 5f4c5088f0
5 changed files with 97 additions and 4 deletions

View file

@ -43,9 +43,6 @@ def register_item(cls):
class BeeItemMixin(SelectableMixin):
"""Base for all items added by the user."""
def sample_color_at(self, pos):
return None
def set_pos_center(self, pos):
"""Sets the position using the item's center as the origin point."""

View file

@ -130,6 +130,9 @@ class BaseItemMixin:
if self.scene():
self.scene().cursor_cleared.emit()
def sample_color_at(self, pos):
return None
class SelectableMixin(BaseItemMixin):
"""Common code for selectable items: Selection outline, handles etc."""
@ -640,7 +643,15 @@ class MultiSelectItem(SelectableMixin,
def selection_action_items(self):
"""The items affected by selection actions like scaling and rotating.
"""
return list(self.scene().selectedItems())
if self.scene():
return list(self.scene().selectedItems())
return []
def lower_behind_selection(self):
items = self.selection_action_items()
if items:
min_z = min(item.zValue() for item in items)
self.setZValue(min_z - self.scene().Z_STEP)
def fit_selection_area(self, rect):
"""Updates itself to fit the given selection area."""

View file

@ -110,6 +110,8 @@ class BeeGraphicsView(MainControlsMixin,
if hasattr(self, 'sample_color_widget'):
self.sample_color_widget.hide()
del self.sample_color_widget
if self.scene.has_multi_selection():
self.scene.multi_select_item.bring_to_front()
def update_window_title(self):
clean = self.undo_stack.isClean()
@ -358,6 +360,11 @@ class BeeGraphicsView(MainControlsMixin,
self.viewport().setCursor(Qt.CursorShape.CrossCursor)
self.active_mode = self.SAMPLE_COLOR_MODE
if self.scene.has_multi_selection():
# We don't want to sample the multi select item, so
# temporarily send it to the back:
self.scene.multi_select_item.lower_behind_selection()
pos = self.mapFromGlobal(self.cursor().pos())
self.sample_color_widget = widgets.SampleColorWidget(
self,

View file

@ -47,6 +47,46 @@ def test_selection_action_items(view):
assert action_items == {item1, item2, view.scene.multi_select_item}
def test_lower_behind_selection_when_selection(view):
item1 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item1)
item1.setSelected(True)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(True)
item3 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item3)
item3.setSelected(False)
item1.setZValue(3)
item2.setZValue(4)
item3.setZValue(1)
view.scene.multi_select_item.setZValue(5)
view.scene.multi_select_item.lower_behind_selection()
assert view.scene.multi_select_item.zValue() == 2.999
def test_lower_behind_selection_when_no_selection(view):
item1 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item1)
item1.setSelected(False)
item2 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item2)
item2.setSelected(False)
item3 = BeePixmapItem(QtGui.QImage())
view.scene.addItem(item3)
item3.setSelected(False)
item1.setZValue(3)
item2.setZValue(4)
item3.setZValue(1)
view.scene.multi_select_item.setZValue(5)
view.scene.multi_select_item.lower_behind_selection()
assert view.scene.multi_select_item.zValue() == 5
def test_fit_selection_area():
item = MultiSelectItem()
item.setScale(5)

View file

@ -658,6 +658,24 @@ def test_on_action_sample_color(view):
view.cancel_active_modes.assert_called_once_with()
def test_on_action_sample_color_when_multi_selection(view, item):
view.scene.addItem(item)
item.setSelected(True)
item2 = BeeTextItem('foo')
view.scene.addItem(item2)
item2.setSelected(True)
view.cancel_active_modes = MagicMock()
view.scene.multi_select_item.lower_behind_selection = MagicMock()
view.on_action_sample_color()
assert view.active_mode == view.SAMPLE_COLOR_MODE
assert isinstance(view.sample_color_widget, widgets.SampleColorWidget)
assert view.viewport().cursor() == Qt.CursorShape.CrossCursor
view.cancel_active_modes.assert_called_once_with()
view.scene.multi_select_item.lower_behind_selection\
.assert_called_once_with()
@patch('PyQt6.QtWidgets.QWidget.create')
@patch('PyQt6.QtWidgets.QWidget.destroy')
@patch('PyQt6.QtWidgets.QWidget.show')
@ -815,6 +833,26 @@ def test_cancel_active_modes_when_sample_color_mode(view):
assert view.viewport().cursor() == Qt.CursorShape.ArrowCursor
def test_cancel_sample_color_mode_when_multi_selection(view, item):
view.scene.addItem(item)
item.setSelected(True)
item2 = BeeTextItem('foo')
view.scene.addItem(item2)
item2.setSelected(True)
view.scene.multi_select_item.bring_to_front = MagicMock()
view.active_mode = view.SAMPLE_COLOR_MODE
view.sample_color_widget = widgets.SampleColorWidget(
view, MagicMock(), MagicMock())
view.viewport().setCursor(Qt.CursorShape.CrossCursor)
view.cancel_active_modes()
assert view.active_mode is None
assert hasattr(view, 'sample_color_widget') is False
assert view.viewport().cursor() == Qt.CursorShape.ArrowCursor
view.scene.multi_select_item.bring_to_front.assert_called_once()
@patch('PyQt6.QtGui.QUndoStack.isClean', return_value=True)
def test_update_window_title_no_changes_no_filename(clear_mock, view):
view.filename = None