Rectangles in incons
This commit is contained in:
@@ -3,12 +3,13 @@ from __future__ import annotations
|
||||
from copy import deepcopy
|
||||
|
||||
from PySide6.QtCore import QEvent, QObject, Qt, Signal
|
||||
from PySide6.QtGui import QKeySequence, QPainter, QShortcut, QUndoCommand, QUndoStack, QWheelEvent
|
||||
from PySide6.QtWidgets import QGraphicsView, QMainWindow, QWidget
|
||||
from PySide6.QtGui import QKeySequence, QPainter, QShortcut, QUndoCommand, QUndoStack, QWheelEvent, QShowEvent
|
||||
from PySide6.QtWidgets import QDialog, QGraphicsView, QMainWindow, QWidget
|
||||
|
||||
from bedit_gui.models import Icon, Shape, ShapeID
|
||||
from bedit_gui.ui.generated.ui_icon_editor_window import Ui_iconEditor
|
||||
from bedit_gui.views.icon_graphics_scene import IconGraphicsScene, RectangleCreationTool
|
||||
from bedit_gui.views.shape_options_dialog import ShapeOptionsDialog
|
||||
|
||||
|
||||
class ChangeIconDraftCommand(QUndoCommand):
|
||||
@@ -39,6 +40,35 @@ class AddShapeCommand(QUndoCommand):
|
||||
self.editor._remove_shape(self.shape_id)
|
||||
|
||||
|
||||
class ChangeShapeCommand(QUndoCommand):
|
||||
def __init__(self, editor: IconEditorWindow, shape_id: ShapeID, old_shape: Shape, new_shape: Shape) -> None:
|
||||
super().__init__(f"Change {new_shape.type}")
|
||||
self.editor = editor
|
||||
self.shape_id = shape_id
|
||||
self.old_shape = deepcopy(old_shape)
|
||||
self.new_shape = deepcopy(new_shape)
|
||||
|
||||
def redo(self) -> None:
|
||||
self.editor._change_shape(self.shape_id, self.new_shape)
|
||||
|
||||
def undo(self) -> None:
|
||||
self.editor._change_shape(self.shape_id, self.old_shape)
|
||||
|
||||
|
||||
class DeleteShapesCommand(QUndoCommand):
|
||||
def __init__(self, editor: IconEditorWindow, shapes: dict[ShapeID, Shape]) -> None:
|
||||
text = "Delete shape" if len(shapes) == 1 else "Delete shapes"
|
||||
super().__init__(text)
|
||||
self.editor = editor
|
||||
self.shapes = deepcopy(shapes)
|
||||
|
||||
def redo(self) -> None:
|
||||
self.editor._remove_shapes(self.shapes)
|
||||
|
||||
def undo(self) -> None:
|
||||
self.editor._restore_shapes(self.shapes)
|
||||
|
||||
|
||||
class IconEditorWindow(QMainWindow):
|
||||
"""Independent icon editing session with its own undo stack."""
|
||||
|
||||
@@ -60,6 +90,8 @@ class IconEditorWindow(QMainWindow):
|
||||
self.scene = IconGraphicsScene(self)
|
||||
self.scene.set_icon(self._icon)
|
||||
self.scene.shape_created.connect(self._shape_created)
|
||||
self.scene.shape_changed.connect(self._shape_changed)
|
||||
self.scene.shape_options_requested.connect(self._show_shape_options)
|
||||
self.scene.tool_active_changed.connect(self._tool_active_changed)
|
||||
self.ui.graphicsView.setScene(self.scene)
|
||||
self.ui.graphicsView.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
@@ -69,6 +101,8 @@ class IconEditorWindow(QMainWindow):
|
||||
self.ui.actionAdd_Rectangle.triggered.connect(self._start_rectangle_tool)
|
||||
self.cancel_tool_shortcut = QShortcut(QKeySequence(Qt.Key.Key_Escape), self)
|
||||
self.cancel_tool_shortcut.activated.connect(self.scene.cancel_creation_tool)
|
||||
self.delete_shortcut = QShortcut(QKeySequence(Qt.Key.Key_Delete), self)
|
||||
self.delete_shortcut.activated.connect(self._delete_selected_shapes)
|
||||
self.zoom_in_shortcut = QShortcut(QKeySequence("Ctrl++"), self)
|
||||
self.zoom_in_alt_shortcut = QShortcut(QKeySequence("Ctrl+="), self)
|
||||
self.zoom_out_shortcut = QShortcut(QKeySequence("Ctrl+-"), self)
|
||||
@@ -90,6 +124,10 @@ class IconEditorWindow(QMainWindow):
|
||||
self.ui.actionUndo.setEnabled(False)
|
||||
self.ui.actionRedo.setEnabled(False)
|
||||
|
||||
def showEvent(self, event: QShowEvent) -> None:
|
||||
super().showEvent(event)
|
||||
self.fit_scene()
|
||||
|
||||
def icon(self) -> Icon:
|
||||
return deepcopy(self._icon)
|
||||
|
||||
@@ -107,7 +145,10 @@ class IconEditorWindow(QMainWindow):
|
||||
self._zoom(1 / self.zoom_step)
|
||||
|
||||
def reset_zoom(self) -> None:
|
||||
self.ui.graphicsView.resetTransform()
|
||||
self.fit_scene()
|
||||
|
||||
def fit_scene(self) -> None:
|
||||
self.ui.graphicsView.fitInView(self.scene.sceneRect(), Qt.AspectRatioMode.KeepAspectRatio)
|
||||
|
||||
def eventFilter(self, watched: QObject, event: QEvent) -> bool:
|
||||
if watched is self.ui.graphicsView.viewport() and isinstance(event, QWheelEvent):
|
||||
@@ -140,9 +181,44 @@ class IconEditorWindow(QMainWindow):
|
||||
self.scene.set_icon(self._icon)
|
||||
self.icon_changed.emit(self.icon())
|
||||
|
||||
def _remove_shapes(self, shapes: dict[ShapeID, Shape]) -> None:
|
||||
for shape_id in shapes:
|
||||
self._icon.shapes.pop(shape_id, None)
|
||||
self.scene.set_icon(self._icon)
|
||||
self.icon_changed.emit(self.icon())
|
||||
|
||||
def _restore_shapes(self, shapes: dict[ShapeID, Shape]) -> None:
|
||||
self._icon.shapes.update(deepcopy(shapes))
|
||||
self.scene.set_icon(self._icon)
|
||||
self.icon_changed.emit(self.icon())
|
||||
|
||||
def _change_shape(self, shape_id: ShapeID, shape: Shape) -> None:
|
||||
self._icon.shapes[shape_id] = deepcopy(shape)
|
||||
self.scene.set_icon(self._icon)
|
||||
self.icon_changed.emit(self.icon())
|
||||
|
||||
def _shape_created(self, shape: Shape) -> None:
|
||||
self.undo_stack.push(AddShapeCommand(self, ShapeID(), shape))
|
||||
|
||||
def _shape_changed(self, shape_id: ShapeID, old_shape: Shape, new_shape: Shape) -> None:
|
||||
self.undo_stack.push(ChangeShapeCommand(self, shape_id, old_shape, new_shape))
|
||||
|
||||
def _delete_selected_shapes(self) -> None:
|
||||
shape_ids = self.scene.selected_shape_ids()
|
||||
shapes = {shape_id: self._icon.shapes[shape_id] for shape_id in shape_ids}
|
||||
if shapes:
|
||||
self.undo_stack.push(DeleteShapesCommand(self, shapes))
|
||||
|
||||
def _show_shape_options(self, shape_id: ShapeID) -> None:
|
||||
old_shape = self._icon.shapes.get(shape_id)
|
||||
if old_shape is None:
|
||||
return
|
||||
dialog = ShapeOptionsDialog(old_shape, self.scene.sceneRect(), self)
|
||||
if dialog.exec() == QDialog.DialogCode.Accepted:
|
||||
new_shape = dialog.shape()
|
||||
if new_shape != old_shape:
|
||||
self.undo_stack.push(ChangeShapeCommand(self, shape_id, old_shape, new_shape))
|
||||
|
||||
def _start_rectangle_tool(self) -> None:
|
||||
layer = max((shape.layer for shape in self._icon.shapes.values()), default=-1) + 1
|
||||
self.scene.set_creation_tool(RectangleCreationTool(self.scene, layer))
|
||||
|
||||
Reference in New Issue
Block a user