Start of the icon editor
This commit is contained in:
123
src/bedit_gui/views/icon_graphics_scene.py
Normal file
123
src/bedit_gui/views/icon_graphics_scene.py
Normal file
@@ -0,0 +1,123 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from PySide6.QtCore import QObject, QPointF, QRectF, Qt, Signal
|
||||
from PySide6.QtGui import QBrush, QColor, QPainterPath, QPen
|
||||
from PySide6.QtWidgets import QGraphicsPathItem, QGraphicsRectItem, QGraphicsScene, QGraphicsSceneMouseEvent
|
||||
|
||||
from bedit_gui.models import Icon, LineType, Rectangle, Shape
|
||||
|
||||
|
||||
class ShapeCreationTool(Protocol):
|
||||
def begin(self, position: QPointF) -> None: ...
|
||||
def update(self, position: QPointF) -> None: ...
|
||||
def finish(self, position: QPointF) -> Shape | None: ...
|
||||
def cancel(self) -> None: ...
|
||||
|
||||
|
||||
class RectangleCreationTool:
|
||||
def __init__(self, scene: QGraphicsScene, layer: int) -> None:
|
||||
self.scene = scene
|
||||
self.layer = layer
|
||||
self.start: QPointF | None = None
|
||||
self.preview: QGraphicsRectItem | None = None
|
||||
|
||||
def begin(self, position: QPointF) -> None:
|
||||
self.start = position
|
||||
self.preview = self.scene.addRect(QRectF(position, position), QPen(Qt.PenStyle.DashLine))
|
||||
|
||||
def update(self, position: QPointF) -> None:
|
||||
if self.preview is not None and self.start is not None:
|
||||
self.preview.setRect(QRectF(self.start, position).normalized())
|
||||
|
||||
def finish(self, position: QPointF) -> Rectangle | None:
|
||||
if self.start is None:
|
||||
return None
|
||||
rect = QRectF(self.start, position).normalized()
|
||||
self.cancel()
|
||||
if rect.width() < 1 or rect.height() < 1:
|
||||
return None
|
||||
return Rectangle(layer=self.layer, pos=(round(rect.x()), round(rect.y())), width=rect.width(), height=rect.height())
|
||||
|
||||
def cancel(self) -> None:
|
||||
if self.preview is not None:
|
||||
self.scene.removeItem(self.preview)
|
||||
self.preview = None
|
||||
self.start = None
|
||||
|
||||
|
||||
class IconGraphicsScene(QGraphicsScene):
|
||||
shape_created = Signal(object)
|
||||
tool_active_changed = Signal(bool)
|
||||
|
||||
def __init__(self, parent: QObject | None = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._tool: ShapeCreationTool | None = None
|
||||
self.setSceneRect(-100, -100, 200, 200)
|
||||
|
||||
def set_icon(self, icon: Icon) -> None:
|
||||
self.cancel_creation_tool()
|
||||
self.clear()
|
||||
for shape in sorted(icon.shapes.values(), key=lambda item: item.layer):
|
||||
self._add_shape_item(shape)
|
||||
|
||||
def set_creation_tool(self, tool: ShapeCreationTool) -> None:
|
||||
self.cancel_creation_tool()
|
||||
self._tool = tool
|
||||
self.tool_active_changed.emit(True)
|
||||
|
||||
def cancel_creation_tool(self) -> None:
|
||||
if self._tool is None:
|
||||
return
|
||||
self._tool.cancel()
|
||||
self._tool = None
|
||||
self.tool_active_changed.emit(False)
|
||||
|
||||
def has_creation_tool(self) -> bool:
|
||||
return self._tool is not None
|
||||
|
||||
def mousePressEvent(self, event: QGraphicsSceneMouseEvent) -> None:
|
||||
if self._tool is not None and event.button() == Qt.MouseButton.LeftButton:
|
||||
self._tool.begin(event.scenePos())
|
||||
event.accept()
|
||||
return
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent) -> None:
|
||||
if self._tool is not None and event.buttons() & Qt.MouseButton.LeftButton:
|
||||
self._tool.update(event.scenePos())
|
||||
event.accept()
|
||||
return
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent) -> None:
|
||||
if self._tool is not None and event.button() == Qt.MouseButton.LeftButton:
|
||||
tool = self._tool
|
||||
shape = tool.finish(event.scenePos())
|
||||
self._tool = None
|
||||
self.tool_active_changed.emit(False)
|
||||
if shape is not None:
|
||||
self.shape_created.emit(shape)
|
||||
event.accept()
|
||||
return
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def _add_shape_item(self, shape: Shape) -> None:
|
||||
if not isinstance(shape, Rectangle):
|
||||
return
|
||||
rect = QRectF(shape.pos[0], shape.pos[1], shape.width, shape.height)
|
||||
path = QPainterPath()
|
||||
path.addRoundedRect(rect, shape.corner_radius, shape.corner_radius)
|
||||
item = QGraphicsPathItem(path)
|
||||
item.setPen(self._pen(shape))
|
||||
item.setBrush(QBrush(QColor(shape.fill_color)))
|
||||
item.setZValue(shape.layer)
|
||||
self.addItem(item)
|
||||
|
||||
@staticmethod
|
||||
def _pen(shape: Rectangle) -> QPen:
|
||||
styles = {LineType.SOLID: Qt.PenStyle.SolidLine, LineType.DASHED: Qt.PenStyle.DashLine, LineType.DOTTED: Qt.PenStyle.DotLine, LineType.DASH_DOT: Qt.PenStyle.DashDotLine}
|
||||
if shape.line_type is LineType.NONE:
|
||||
return QPen(Qt.PenStyle.NoPen)
|
||||
return QPen(QColor(shape.line_color), shape.line_thickness, styles[shape.line_type])
|
||||
Reference in New Issue
Block a user