@@ -41,9 +41,12 @@ def __init__(self) -> None:
4141 self ._brush_size : int = 10
4242 self ._brush_color : str = "A"
4343 self ._brush_shape : str = "A"
44- self ._brush_pen : Optional [ QPen ] = None
44+ self ._brush_pen : QPen = self . _createBrushPen ()
4545
4646 self ._mouse_held : bool = False
47+ self ._ctrl_held : bool = False
48+ self ._shift_held : bool = False
49+
4750 self ._last_text_coords : Optional [Tuple [int , int ]] = None
4851
4952 def _createBrushPen (self ) -> QPen :
@@ -72,7 +75,10 @@ def _createStrokeImage(self, x0: float, y0: float, x1: float, y1: float) -> Tupl
7275 painter = QPainter (stroke_image )
7376 painter .setRenderHint (QPainter .RenderHint .Antialiasing , False )
7477 painter .setPen (self ._brush_pen )
75- painter .drawLine (int (x0 - start_x ), int (y0 - start_y ), int (x1 - start_x ), int (y1 - start_y ))
78+ if xdiff == 0 and ydiff == 0 :
79+ painter .drawPoint (int (x0 - start_x ), int (y0 - start_y ))
80+ else :
81+ painter .drawLine (int (x0 - start_x ), int (y0 - start_y ), int (x1 - start_x ), int (y1 - start_y ))
7682 painter .end ()
7783
7884 return stroke_image , (start_x , start_y )
@@ -96,6 +102,20 @@ def setBrushShape(self, brush_shape: str) -> None:
96102 self ._brush_shape = brush_shape
97103 self ._brush_pen = self ._createBrushPen ()
98104
105+ def undoStackAction (self , redo_instead : bool ) -> bool :
106+ paintview = Application .getInstance ().getController ().getActiveView ()
107+ if paintview is None or paintview .getPluginId () != "PaintTool" :
108+ return False
109+ paintview = cast (PaintView , paintview )
110+ if redo_instead :
111+ paintview .redoStroke ()
112+ else :
113+ paintview .undoStroke ()
114+ nodes = Selection .getAllSelectedObjects ()
115+ if len (nodes ) > 0 :
116+ Application .getInstance ().getController ().getScene ().sceneChanged .emit (nodes [0 ])
117+ return True
118+
99119 @staticmethod
100120 def _get_intersect_ratio_via_pt (a : numpy .ndarray , pt : numpy .ndarray , b : numpy .ndarray , c : numpy .ndarray ) -> float :
101121 # compute the intersection of (param) A - pt with (param) B - (param) C
@@ -154,6 +174,10 @@ def event(self, event: Event) -> bool:
154174 super ().event (event )
155175
156176 controller = Application .getInstance ().getController ()
177+ nodes = Selection .getAllSelectedObjects ()
178+ if len (nodes ) <= 0 :
179+ return False
180+ node = nodes [0 ]
157181
158182 # Make sure the displayed values are updated if the bounding box of the selected mesh(es) changes
159183 if event .type == Event .ToolActivateEvent :
@@ -166,7 +190,27 @@ def event(self, event: Event) -> bool:
166190 controller .setActiveView ("SolidView" )
167191 return True
168192
169- if event .type == Event .KeyPressEvent and cast (KeyEvent , event ).key == KeyEvent .ShiftKey :
193+ if event .type == Event .KeyPressEvent :
194+ evt = cast (KeyEvent , event )
195+ if evt .key == KeyEvent .ControlKey :
196+ self ._ctrl_held = True
197+ return True
198+ if evt .key == KeyEvent .ShiftKey :
199+ self ._shift_held = True
200+ return True
201+ return False
202+
203+ if event .type == Event .KeyReleaseEvent :
204+ evt = cast (KeyEvent , event )
205+ if evt .key == KeyEvent .ControlKey :
206+ self ._ctrl_held = False
207+ return True
208+ if evt .key == KeyEvent .ShiftKey :
209+ self ._shift_held = False
210+ return True
211+ if evt .key == Qt .Key .Key_L and self ._ctrl_held :
212+ # NOTE: Ctrl-L is used here instead of Ctrl-Z, as the latter is the application-wide one that takes precedence.
213+ return self .undoStackAction (self ._shift_held )
170214 return False
171215
172216 if event .type == Event .MouseReleaseEvent and self ._controller .getToolsEnabled ():
@@ -201,10 +245,6 @@ def event(self, event: Event) -> bool:
201245 if not camera :
202246 return False
203247
204- node = Selection .getAllSelectedObjects ()[0 ]
205- if node is None :
206- return False
207-
208248 if node != self ._node_cache :
209249 if self ._node_cache is not None :
210250 self ._node_cache .transformationChanged .disconnect (self ._nodeTransformChanged )
0 commit comments