diff --git a/src/Mod/Draft/draftguitools/gui_arcs.py b/src/Mod/Draft/draftguitools/gui_arcs.py index 01599b376d..6af896274b 100644 --- a/src/Mod/Draft/draftguitools/gui_arcs.py +++ b/src/Mod/Draft/draftguitools/gui_arcs.py @@ -292,36 +292,7 @@ class Arc(gui_base_original.Creator): else: # choose second angle self.step = 4 self.drawArc() - - self.updateHints() - - def getHints(self): - hint_global = Gui.InputHint(translate("draft", "%1 toggle global"), Gui.UserInput.KeyG) - hint_continue = Gui.InputHint(translate("draft", "%1 toggle continue"), Gui.UserInput.KeyN) - - if self.step == 0: - return [ - Gui.InputHint(translate("draft", "%1 pick center"), Gui.UserInput.MouseLeft), - hint_global, - hint_continue, - ] - elif self.step == 1: - return [ - Gui.InputHint(translate("draft", "%1 pick radius"), Gui.UserInput.MouseLeft), - hint_continue, - ] - elif self.step == 2: - return [ - Gui.InputHint(translate("draft", "%1 pick starting angle"), Gui.UserInput.MouseLeft), - hint_continue, - ] - elif self.step == 3: - return [ - Gui.InputHint(translate("draft", "%1 pick aperture"), Gui.UserInput.MouseLeft), - hint_continue, - ] - else: - return [] + self.update_hints() def drawArc(self): """Actually draw the arc object.""" @@ -436,6 +407,7 @@ class Arc(gui_base_original.Creator): self.step = 1 self.ui.setNextFocus() _toolmsg(translate("draft", "Pick radius")) + self.update_hints() def numericRadius(self, rad): """Validate the entry radius in the user interface. @@ -494,6 +466,29 @@ class Arc(gui_base_original.Creator): self.angle = math.radians(rad) self.step = 4 self.drawArc() + self.update_hints() + + def get_hints(self): + if self.step == 0: + hints = [ + Gui.InputHint(translate("draft", "%1 pick center"), Gui.UserInput.MouseLeft) + ] + elif self.step == 1: + hints = [ + Gui.InputHint(translate("draft", "%1 pick radius"), Gui.UserInput.MouseLeft) + ] + elif self.step == 2: + hints = [ + Gui.InputHint(translate("draft", "%1 pick start angle"), Gui.UserInput.MouseLeft) + ] + else: + hints = [ + Gui.InputHint(translate("draft", "%1 pick aperture"), Gui.UserInput.MouseLeft) + ] + return hints \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() Gui.addCommand('Draft_Arc', Arc()) @@ -534,6 +529,7 @@ class Arc_3Points(gui_base.GuiCommandBase): Gui.Snapper.ui.setTitle(title=translate("draft", "Arc From 3 Points"), icon="Draft_Arc_3Points") Gui.Snapper.ui.continueCmd.show() + self.update_hints() def getPoint(self, point, info): """Get the point by clicking on the 3D view. @@ -578,6 +574,7 @@ class Arc_3Points(gui_base.GuiCommandBase): Gui.Snapper.ui.setTitle(title=translate("draft", "Arc From 3 Points"), icon="Draft_Arc_3Points") Gui.Snapper.ui.continueCmd.show() + self.update_hints() else: # If three points were already picked in the 3D view @@ -631,6 +628,24 @@ class Arc_3Points(gui_base.GuiCommandBase): if cont or (cont is None and Gui.Snapper.ui and Gui.Snapper.ui.continueMode): self.Activated() + def get_hints(self): + if len(self.points) == 0: + hints = [ + Gui.InputHint(translate("draft", "%1 pick first point"), Gui.UserInput.MouseLeft) + ] + elif len(self.points) == 1: + hints = [ + Gui.InputHint(translate("draft", "%1 pick second point"), Gui.UserInput.MouseLeft) + ] + else: + hints = [ + Gui.InputHint(translate("draft", "%1 pick third point"), Gui.UserInput.MouseLeft) + ] + return hints \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() + Draft_Arc_3Points = Arc_3Points Gui.addCommand('Draft_Arc_3Points', Arc_3Points()) diff --git a/src/Mod/Draft/draftguitools/gui_base.py b/src/Mod/Draft/draftguitools/gui_base.py index 3cdc4c8bce..abd9860093 100644 --- a/src/Mod/Draft/draftguitools/gui_base.py +++ b/src/Mod/Draft/draftguitools/gui_base.py @@ -29,6 +29,8 @@ ## \addtogroup draftguitools # @{ +from PySide import QtCore + import FreeCAD as App import FreeCADGui as Gui from draftguitools import gui_trackers as trackers @@ -171,6 +173,12 @@ class GuiCommandBase: _toolmsg("{}".format(16*"-")) _toolmsg("GuiCommand: {}".format(self.featureName)) + def update_hints(self): + Gui.HintManager.show(*self.get_hints()) + + def get_hints(self): + return [] + def finish(self): """Terminate the active command by committing the list of commands. @@ -194,6 +202,8 @@ class GuiCommandBase: todo.ToDo.delayCommit(self.commit_list) self.commit_list = [] + QtCore.QTimer.singleShot(0, Gui.HintManager.hide) + def commit(self, name, func): """Store actions to be committed to the document. diff --git a/src/Mod/Draft/draftguitools/gui_base_original.py b/src/Mod/Draft/draftguitools/gui_base_original.py index 27789bc5a3..3d1e6a448e 100644 --- a/src/Mod/Draft/draftguitools/gui_base_original.py +++ b/src/Mod/Draft/draftguitools/gui_base_original.py @@ -33,7 +33,6 @@ of the DraftToolBar, the Snapper, and the working plane. ## \addtogroup draftguitools # @{ - from PySide import QtCore import FreeCAD as App @@ -137,15 +136,13 @@ class DraftTool: _toolmsg("GuiCommand: {}".format(self.featureName)) # update hints after the tool is fully initialized - QtCore.QTimer.singleShot(0, self.updateHints) + QtCore.QTimer.singleShot(0, self.update_hints) - def updateHints(self): - Gui.HintManager.show(*self.getHints()) + def update_hints(self): + Gui.HintManager.show(*self.get_hints()) - def getHints(self): - return [ - Gui.InputHint("%1 constrain", Gui.UserInput.KeyShift) - ] + def get_hints(self): + return [] def end_callbacks(self, call): try: @@ -195,7 +192,7 @@ class DraftTool: todo.ToDo.delayCommit(self.commitList) self.commitList = [] - Gui.HintManager.hide() + QtCore.QTimer.singleShot(0, Gui.HintManager.hide) def commit(self, name, func): """Store actions in the commit list to be run later. diff --git a/src/Mod/Draft/draftguitools/gui_beziers.py b/src/Mod/Draft/draftguitools/gui_beziers.py index 3d000d886e..ca0729542c 100644 --- a/src/Mod/Draft/draftguitools/gui_beziers.py +++ b/src/Mod/Draft/draftguitools/gui_beziers.py @@ -47,7 +47,7 @@ from draftutils import gui_utils from draftutils import params from draftutils import todo from draftutils import utils -from draftutils.messages import _err, _msg, _toolmsg +from draftutils.messages import _err, _toolmsg from draftutils.translate import translate @@ -134,8 +134,6 @@ class BezCurve(gui_lines.Line): if (self.point-self.node[0]).Length < utils.tolerance(): self.undolast() self.finish(cont=None, closed=True) - _msg(translate("draft", - "Bézier curve has been closed")) def undolast(self): """Undo last line segment.""" @@ -143,7 +141,7 @@ class BezCurve(gui_lines.Line): self.node.pop() self.bezcurvetrack.update(self.node, degree=self.degree) self.obj.Shape = self.updateShape(self.node) - _msg(translate("draft", "Last point has been removed")) + self.update_hints() def drawUpdate(self, point): """Draw and update to the curve.""" @@ -155,6 +153,7 @@ class BezCurve(gui_lines.Line): else: self.obj.Shape = self.updateShape(self.node) _toolmsg(translate("draft", "Pick next point")) + self.update_hints() def updateShape(self, pts): """Create shape for display during creation process.""" @@ -343,7 +342,6 @@ class CubicBezCurve(gui_lines.Line): _sym = 2 * self.node[0] - self.node[1] self.node.append(_sym) self.finish(cont=None, closed=True) - _msg(translate("draft", "Bézier curve has been closed")) # Release the held button if arg["State"] == "UP" and arg["Button"] == "BUTTON1": if arg["Position"] == self.pos: @@ -378,7 +376,7 @@ class CubicBezCurve(gui_lines.Line): self.node.pop() self.bezcurvetrack.update(self.node, degree=self.degree) self.obj.Shape = self.updateShape(self.node) - _msg(translate("draft", "Last point has been removed")) + self.update_hints() def drawUpdate(self, point): """Create shape for display during creation process.""" @@ -391,6 +389,7 @@ class CubicBezCurve(gui_lines.Line): # is a knot self.obj.Shape = self.updateShape(self.node[:-1]) _toolmsg(translate("draft", "Click and drag to define next knot")) + self.update_hints() def updateShape(self, pts): """Create shape for display during creation process.""" @@ -477,6 +476,17 @@ class CubicBezCurve(gui_lines.Line): if cont or (cont is None and self.ui and self.ui.continueMode): self.Activated() + def get_hints(self): + if len(self.node) < 2: + return [Gui.InputHint( + translate("draft", "%1 click and drag to define first point and knot"), + Gui.UserInput.MouseLeft + )] + return [Gui.InputHint( + translate("draft", "%1 click and drag to define next point and knot"), + Gui.UserInput.MouseLeft + )] + Gui.addCommand('Draft_CubicBezCurve', CubicBezCurve()) diff --git a/src/Mod/Draft/draftguitools/gui_ellipses.py b/src/Mod/Draft/draftguitools/gui_ellipses.py index 48defdf513..ded39d51ea 100644 --- a/src/Mod/Draft/draftguitools/gui_ellipses.py +++ b/src/Mod/Draft/draftguitools/gui_ellipses.py @@ -206,6 +206,21 @@ class Ellipse(gui_base_original.Creator): self.rect.on() if self.planetrack: self.planetrack.set(point) + self.update_hints() + + def get_hints(self): + if len(self.node) == 0: + hints = [ + Gui.InputHint(translate("draft", "%1 pick first point"), Gui.UserInput.MouseLeft) + ] + else: + hints = [ + Gui.InputHint(translate("draft", "%1 pick opposite point"), Gui.UserInput.MouseLeft) + ] + return hints \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() Gui.addCommand('Draft_Ellipse', Ellipse()) diff --git a/src/Mod/Draft/draftguitools/gui_lines.py b/src/Mod/Draft/draftguitools/gui_lines.py index 561da6ce34..199211154a 100644 --- a/src/Mod/Draft/draftguitools/gui_lines.py +++ b/src/Mod/Draft/draftguitools/gui_lines.py @@ -123,7 +123,7 @@ class Line(gui_base_original.Creator): self.ui.redraw() self.pos = arg["Position"] self.node.append(self.point) - self.drawSegment(self.point) + self.drawUpdate(self.point) if self.mode == "line" and len(self.node) == 2: self.finish(cont=None, closed=False) if len(self.node) > 2: @@ -227,8 +227,9 @@ class Line(gui_base_original.Creator): # DNC: report on removal # _toolmsg(translate("draft", "Removing last point")) _toolmsg(translate("draft", "Pick next point")) + self.update_hints() - def drawSegment(self, point): + def drawUpdate(self, point): """Draws new line segment.""" import Part if self.planetrack and self.node: @@ -250,6 +251,7 @@ class Line(gui_base_original.Creator): newshape = currentshape.fuse(newseg) self.obj.Shape = newshape _toolmsg(translate("draft", "Pick next point")) + self.update_hints() def wipe(self): """Remove all previous segments and starts from last point.""" @@ -260,6 +262,7 @@ class Line(gui_base_original.Creator): if self.planetrack: self.planetrack.set(self.node[0]) _toolmsg(translate("draft", "Pick next point")) + self.update_hints() def orientWP(self): """Orient the working plane.""" @@ -282,11 +285,36 @@ class Line(gui_base_original.Creator): """ self.point = App.Vector(numx, numy, numz) self.node.append(self.point) - self.drawSegment(self.point) + self.drawUpdate(self.point) if self.mode == "line" and len(self.node) == 2: self.finish(cont=None, closed=False) self.ui.setNextFocus() + def get_hints(self): + if len(self.node) == 0: + hints = [ + Gui.InputHint(translate("draft", "%1 pick first point"), Gui.UserInput.MouseLeft) + ] + elif self.mode == "line": + hints = [ + Gui.InputHint(translate("draft", "%1 pick second point"), Gui.UserInput.MouseLeft) + ] + elif len(self.node) > 2: + hints = [ + Gui.InputHint( + translate("draft", "%1 pick next point, snap to first point to close"), + Gui.UserInput.MouseLeft + ) + ] + else: + hints = [ + Gui.InputHint(translate("draft", "%1 pick next point"), Gui.UserInput.MouseLeft) + ] + return hints \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() + Gui.addCommand('Draft_Line', Line()) diff --git a/src/Mod/Draft/draftguitools/gui_points.py b/src/Mod/Draft/draftguitools/gui_points.py index 2037080f18..1d6addcbd9 100644 --- a/src/Mod/Draft/draftguitools/gui_points.py +++ b/src/Mod/Draft/draftguitools/gui_points.py @@ -42,6 +42,7 @@ import FreeCAD as App import FreeCADGui as Gui import Draft_rc from draftguitools import gui_base_original +from draftguitools import gui_tool_utils from draftutils import gui_utils from draftutils import params from draftutils import todo @@ -164,6 +165,12 @@ class Point(gui_base_original.Creator): if cont or (cont is None and self.ui and self.ui.continueMode): self.Activated() + def get_hints(self): + return [Gui.InputHint(translate("draft", "%1 pick point"), Gui.UserInput.MouseLeft)] \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() + Gui.addCommand('Draft_Point', Point()) diff --git a/src/Mod/Draft/draftguitools/gui_polygons.py b/src/Mod/Draft/draftguitools/gui_polygons.py index 60bf530dbc..e437458712 100644 --- a/src/Mod/Draft/draftguitools/gui_polygons.py +++ b/src/Mod/Draft/draftguitools/gui_polygons.py @@ -210,6 +210,7 @@ class Polygon(gui_base_original.Creator): _toolmsg(translate("draft", "Pick radius")) if self.planetrack: self.planetrack.set(self.point) + self.update_hints() elif self.step == 1: # choose radius self.drawPolygon() @@ -268,6 +269,7 @@ class Polygon(gui_base_original.Creator): self.step = 1 self.ui.radiusValue.setFocus() _toolmsg(translate("draft", "Pick radius")) + self.update_hints() def numericRadius(self, rad): """Validate the entry radius in the user interface. @@ -298,6 +300,20 @@ class Polygon(gui_base_original.Creator): self.center = cir[-1].Center self.drawPolygon() + def get_hints(self): + if self.step == 0: + hints = [ + Gui.InputHint(translate("draft", "%1 pick center"), Gui.UserInput.MouseLeft) + ] + else: + hints = [ + Gui.InputHint(translate("draft", "%1 pick radius"), Gui.UserInput.MouseLeft) + ] + return hints \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() + Gui.addCommand('Draft_Polygon', Polygon()) diff --git a/src/Mod/Draft/draftguitools/gui_rectangles.py b/src/Mod/Draft/draftguitools/gui_rectangles.py index db30c982ed..ecdaf15019 100644 --- a/src/Mod/Draft/draftguitools/gui_rectangles.py +++ b/src/Mod/Draft/draftguitools/gui_rectangles.py @@ -202,6 +202,21 @@ class Rectangle(gui_base_original.Creator): self.rect.on() if self.planetrack: self.planetrack.set(point) + self.update_hints() + + def get_hints(self): + if len(self.node) == 0: + hints = [ + Gui.InputHint(translate("draft", "%1 pick first point"), Gui.UserInput.MouseLeft) + ] + else: + hints = [ + Gui.InputHint(translate("draft", "%1 pick opposite point"), Gui.UserInput.MouseLeft) + ] + return hints \ + + gui_tool_utils._get_hint_xyz_constrain() \ + + gui_tool_utils._get_hint_mod_constrain() \ + + gui_tool_utils._get_hint_mod_snap() Gui.addCommand('Draft_Rectangle', Rectangle()) diff --git a/src/Mod/Draft/draftguitools/gui_shapestrings.py b/src/Mod/Draft/draftguitools/gui_shapestrings.py index 0436004919..2ea8b19c5b 100644 --- a/src/Mod/Draft/draftguitools/gui_shapestrings.py +++ b/src/Mod/Draft/draftguitools/gui_shapestrings.py @@ -69,6 +69,7 @@ class ShapeString(gui_base.GuiCommandBase): task = Gui.Control.showDialog(self.ui) task.setDocumentName(Gui.ActiveDocument.Document.Name) task.setAutoCloseOnDeletedDocument(True) + self.ui.update_hints() def finish(self): try: diff --git a/src/Mod/Draft/draftguitools/gui_splines.py b/src/Mod/Draft/draftguitools/gui_splines.py index 6bbb6db2b9..212e2dc37e 100644 --- a/src/Mod/Draft/draftguitools/gui_splines.py +++ b/src/Mod/Draft/draftguitools/gui_splines.py @@ -42,7 +42,7 @@ import draftguitools.gui_tool_utils as gui_tool_utils import draftguitools.gui_lines as gui_lines import draftguitools.gui_trackers as trackers -from draftutils.messages import _msg, _err, _toolmsg +from draftutils.messages import _err, _toolmsg from draftutils.translate import translate @@ -121,7 +121,6 @@ class BSpline(gui_lines.Line): if (self.point - self.node[0]).Length < utils.tolerance(): self.undolast() self.finish(cont=None, closed=True) - _msg(translate("draft", "Spline has been closed")) def undolast(self): """Undo last line segment.""" @@ -132,7 +131,7 @@ class BSpline(gui_lines.Line): spline = Part.BSplineCurve() spline.interpolate(self.node, False) self.obj.Shape = spline.toShape() - _msg(translate("draft", "Last point has been removed")) + self.update_hints() def drawUpdate(self, point): """Draw and update to the spline.""" @@ -147,6 +146,7 @@ class BSpline(gui_lines.Line): spline.interpolate(self.node, False) self.obj.Shape = spline.toShape() _toolmsg(translate("draft", "Pick next point")) + self.update_hints() def finish(self, cont=False, closed=False): """Terminate the operation and close the spline if asked. diff --git a/src/Mod/Draft/draftguitools/gui_tool_utils.py b/src/Mod/Draft/draftguitools/gui_tool_utils.py index b75645606d..4190bb4d37 100644 --- a/src/Mod/Draft/draftguitools/gui_tool_utils.py +++ b/src/Mod/Draft/draftguitools/gui_tool_utils.py @@ -34,6 +34,8 @@ as they operate on selections and graphical properties. ## \addtogroup draftguitools # @{ +import re + import FreeCAD as App import FreeCADGui as Gui import WorkingPlane @@ -41,6 +43,8 @@ from draftutils import gui_utils from draftutils import params from draftutils import utils from draftutils.messages import _wrn +from draftutils.translate import translate + # Set modifier keys from the parameter database MODS = ["shift", "ctrl", "alt"] @@ -58,6 +62,66 @@ def get_mod_alt_key(): return MODS[params.get_param("modalt")] +_HINT_MOD_KEYS = [Gui.UserInput.KeyShift, Gui.UserInput.KeyControl, Gui.UserInput.KeyAlt] + + +# To allows for easy concatenation the _get_hint_* functions +# always return a list (with a single item or an empty list). + +def _get_hint_mod_constrain(): + key = _HINT_MOD_KEYS[params.get_param("modconstrain")] + return [Gui.InputHint(translate("draft", "%1 constrain"), key)] + + +def _get_hint_mod_snap(): + if params.get_param("alwaysSnap"): + return [] + key = _HINT_MOD_KEYS[params.get_param("modsnap")] + return [Gui.InputHint(translate("draft", "%1 snap"), key)] + + +def _get_hint_xyz_constrain(): + pattern = re.compile("[A-Z]") + shortcut_x = params.get_param("inCommandShortcutRestrictX").upper() + shortcut_y = params.get_param("inCommandShortcutRestrictY").upper() + shortcut_z = params.get_param("inCommandShortcutRestrictZ").upper() + if pattern.fullmatch(shortcut_x) \ + and pattern.fullmatch(shortcut_y) \ + and pattern.fullmatch(shortcut_z): + key_x = getattr(Gui.UserInput, "Key" + shortcut_x) + key_y = getattr(Gui.UserInput, "Key" + shortcut_y) + key_z = getattr(Gui.UserInput, "Key" + shortcut_z) + return [Gui.InputHint(translate("draft", "%1/%2/%3 switch constraint"), key_x, key_y, key_z)] + return [] + + +def _get_hint_relative(): + pattern = re.compile("[A-Z]") + shortcut = params.get_param("inCommandShortcutRelative").upper() + if pattern.fullmatch(shortcut): + key = getattr(Gui.UserInput, "Key" + shortcut) + return [Gui.InputHint(translate("draft", "%1 toggle relative"), key)] + return [] + + +def _get_hint_global(): + pattern = re.compile("[A-Z]") + shortcut = params.get_param("inCommandShortcutGlobal").upper() + if pattern.fullmatch(shortcut): + key = getattr(Gui.UserInput, "Key" + shortcut) + return [Gui.InputHint(translate("draft", "%1 toggle global"), key)] + return [] + + +def _get_hint_continue(): + pattern = re.compile("[A-Z]") + shortcut = params.get_param("inCommandShortcutContinue").upper() + if pattern.fullmatch(shortcut): + key = getattr(Gui.UserInput, "Key" + shortcut) + return [Gui.InputHint(translate("draft", "%1 toggle continue"), key)] + return [] + + def format_unit(exp, unit="mm"): """Return a formatting string to set a number to the correct unit.""" return App.Units.Quantity(exp, App.Units.Length).UserString diff --git a/src/Mod/Draft/drafttaskpanels/task_shapestring.py b/src/Mod/Draft/drafttaskpanels/task_shapestring.py index 753f99baf5..30384656a8 100644 --- a/src/Mod/Draft/drafttaskpanels/task_shapestring.py +++ b/src/Mod/Draft/drafttaskpanels/task_shapestring.py @@ -104,10 +104,12 @@ class ShapeStringTaskPanel: if not self.pointPicked: self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg, noTracker=True) self.display_point(self.point) + self.update_hints() elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): self.display_point(self.point) self.pointPicked = True + self.update_hints() def change_coord_labels(self): if self.global_mode: @@ -153,6 +155,7 @@ class ShapeStringTaskPanel: self.point = self.wp.position self.pointPicked = False self.display_point(self.point) + self.update_hints() def set_file(self, val): """Assign the selected font file.""" @@ -199,6 +202,17 @@ class ShapeStringTaskPanel: self.text = val params.set_param("ShapeStringText", self.text) + def update_hints(self): + if self.pointPicked: + Gui.HintManager.hide() + else: + Gui.HintManager.show( + Gui.InputHint( + translate("draft", "%1 pick point"), + Gui.UserInput.MouseLeft + ) + ) + class ShapeStringTaskPanelCmd(ShapeStringTaskPanel): """Task panel for Draft_ShapeString."""