diff --git a/src/Gui/SelectionView.cpp b/src/Gui/SelectionView.cpp index ee8e5039b0..0d7ec79114 100644 --- a/src/Gui/SelectionView.cpp +++ b/src/Gui/SelectionView.cpp @@ -55,8 +55,9 @@ using namespace Gui::DockWnd; SelectionView::SelectionView(Gui::Document* pcDocument, QWidget *parent) : DockWindow(pcDocument,parent) - , SelectionObserver(false,0) + , SelectionObserver(true,0) , x(0.0f), y(0.0f), z(0.0f) + , openedAutomatically(false) { setWindowTitle(tr("Selection View")); @@ -124,6 +125,22 @@ void SelectionView::leaveEvent(QEvent *) /// @cond DOXERR void SelectionView::onSelectionChanged(const SelectionChanges &Reason) { + ParameterGrp::handle hGrp = App::GetApplication().GetUserParameter().GetGroup("BaseApp") + ->GetGroup("Preferences")->GetGroup("Selection"); + bool autoShow = hGrp->GetBool("AutoShowSelectionView", false); + hGrp->SetBool("AutoShowSelectionView", autoShow); // Remove this line once the preferences window item is implemented + + if (autoShow) { + if (!parentWidget()->isVisible() && Selection().hasSelection()) { + parentWidget()->show(); + openedAutomatically = true; + } + else if (openedAutomatically && !Selection().hasSelection()) { + parentWidget()->hide(); + openedAutomatically = false; + } + } + QString selObject; QTextStream str(&selObject); if (Reason.Type == SelectionChanges::AddSelection) { @@ -604,14 +621,10 @@ bool SelectionView::onMsg(const char* /*pMsg*/,const char** /*ppReturn*/) } void SelectionView::hideEvent(QHideEvent *ev) { - FC_TRACE(this << " detaching selection observer"); - this->detachSelection(); DockWindow::hideEvent(ev); } void SelectionView::showEvent(QShowEvent *ev) { - FC_TRACE(this << " attaching selection observer"); - this->attachSelection(); enablePickList->setChecked(Selection().needPickedList()); Gui::DockWindow::showEvent(ev); } diff --git a/src/Gui/SelectionView.h b/src/Gui/SelectionView.h index ecb9a1dad5..da72d37298 100644 --- a/src/Gui/SelectionView.h +++ b/src/Gui/SelectionView.h @@ -110,6 +110,7 @@ private: private: float x,y,z; std::vector searchList; + bool openedAutomatically; }; } // namespace DockWnd diff --git a/src/Mod/Arch/ArchCutPlane.py b/src/Mod/Arch/ArchCutPlane.py index fbc804a647..7565f69cd6 100644 --- a/src/Mod/Arch/ArchCutPlane.py +++ b/src/Mod/Arch/ArchCutPlane.py @@ -64,7 +64,8 @@ def cutComponentwithPlane(archObject, cutPlane, sideFace): obj.ViewObject.ShapeColor = (1.00,0.00,0.00) obj.ViewObject.Transparency = 75 if "Additions" in archObject.Object.PropertiesList: - return ArchCommands.removeComponents(obj,archObject.Object) + ArchCommands.removeComponents(obj,archObject.Object) + return None else: cutObj = FreeCAD.ActiveDocument.addObject("Part::Cut","CutPlane") cutObj.Base = archObject.Object diff --git a/src/Mod/Arch/ArchWall.py b/src/Mod/Arch/ArchWall.py index 1feef163a3..37ebb77c8b 100644 --- a/src/Mod/Arch/ArchWall.py +++ b/src/Mod/Arch/ArchWall.py @@ -878,8 +878,7 @@ class _Wall(ArchComponent.Component): else: cuts2.append(sh) offset += (obj.BlockLength.Value + obj.Joint.Value) - else: - offset -= (edge.Length - obj.Joint.Value) + offset -= (edge.Length - obj.Joint.Value) if isinstance(bplates,list): bplates = bplates[0] diff --git a/src/Mod/Arch/InitGui.py b/src/Mod/Arch/InitGui.py index ca778b1cee..5f1927b60f 100644 --- a/src/Mod/Arch/InitGui.py +++ b/src/Mod/Arch/InitGui.py @@ -128,7 +128,6 @@ class ArchWorkbench(FreeCADGui.Workbench): import RebarTools except Exception: del self.archtools[3] # remove "Arch_Rebar_Submenu" - pass else: class RebarGroupCommand: def GetCommands(self): diff --git a/src/Mod/Draft/draftmake/make_dimension.py b/src/Mod/Draft/draftmake/make_dimension.py index 9cd4ebfe8d..1bc3d90fda 100644 --- a/src/Mod/Draft/draftmake/make_dimension.py +++ b/src/Mod/Draft/draftmake/make_dimension.py @@ -509,7 +509,7 @@ def make_radial_dimension_obj(edge_object, index=1, mode="radius", def make_angular_dimension(center=App.Vector(0, 0, 0), - angles=[0, 90], + angles=None, # If None, set to [0,90] dim_line=App.Vector(10, 10, 0), normal=None): """Create an angular dimension from the given center and angles. @@ -555,6 +555,10 @@ def make_angular_dimension(center=App.Vector(0, 0, 0), _name = "make_angular_dimension" utils.print_header(_name, "Angular dimension") + # Prevent later modification of a default parameter by using a placeholder + if angles is None: + angles = [0, 90] + found, doc = utils.find_doc(App.activeDocument()) if not found: _err(translate("draft","No active document. Aborting.")) diff --git a/src/Mod/Draft/drafttests/draft_test_objects.py b/src/Mod/Draft/drafttests/draft_test_objects.py index a4f10caadf..1544b7089a 100644 --- a/src/Mod/Draft/drafttests/draft_test_objects.py +++ b/src/Mod/Draft/drafttests/draft_test_objects.py @@ -1,5 +1,6 @@ # *************************************************************************** -# * (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2021 FreeCAD Developers * # * * # * This file is part of the FreeCAD CAx development system. * # * * @@ -40,7 +41,6 @@ Or load it as a module and use the defined function. ## \addtogroup drafttests # @{ import datetime -import os import FreeCAD as App import Part @@ -66,17 +66,7 @@ def _set_text(text_list, position): def _create_frame(doc=None): - """Draw a frame with information on the version of the software. - - It includes the date created, the version, the release type, - and the branch. - - Parameters - ---------- - doc: App::Document, optional - It defaults to `None`, which then defaults to the current - active document, or creates a new document. - """ + """Draw a frame with information on the version of the software.""" if not doc: doc = App.activeDocument() if not doc: @@ -101,10 +91,10 @@ def _create_frame(doc=None): record.ViewObject.FontSize = 400 record.ViewObject.TextColor = (0.0, 0.0, 0.0) - p1 = Vector(-1000, -3500, 0) - p2 = Vector(20000, -3500, 0) - p3 = Vector(20000, 8500, 0) - p4 = Vector(-1000, 8500, 0) + p1 = Vector(-1000, -4000, 0) + p2 = Vector(17000, -4000, 0) + p3 = Vector(17000, 8000, 0) + p4 = Vector(-1000, 8000, 0) poly = Part.makePolygon([p1, p2, p3, p4, p1]) frame = doc.addObject("Part::Feature", "Frame") @@ -112,199 +102,135 @@ def _create_frame(doc=None): def _create_objects(doc=None, - font_file="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"): - """Create the objects of the test file. - - Parameters - ---------- - doc: App::Document, optional - It defaults to `None`, which then defaults to the current - active document, or creates a new document. - """ + font_file=None, + hatch_file=None, + hatch_name=None): + """Create the objects of the test file.""" if not doc: doc = App.activeDocument() if not doc: doc = App.newDocument() - # Line, wire, and fillet + # Drafting ############################################################## + + # Line _msg(16 * "-") _msg("Line") Draft.make_line(Vector(0, 0, 0), Vector(500, 500, 0)) - t_xpos = -50 - t_ypos = -200 - _set_text(["Line"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Line"], Vector(0, -200, 0)) + # Wire _msg(16 * "-") _msg("Wire") - Draft.make_wire([Vector(500, 0, 0), - Vector(1000, 500, 0), - Vector(1000, 1000, 0)]) - t_xpos += 500 - _set_text(["Wire"], Vector(t_xpos, t_ypos, 0)) + Draft.make_wire([Vector(1000, 0, 0), + Vector(1500, 250, 0), + Vector(1500, 500, 0)]) + _set_text(["Wire"], Vector(1000, -200, 0)) + # Fillet _msg(16 * "-") _msg("Fillet") - line_h_1 = Draft.make_line(Vector(1500, 0, 0), Vector(1500, 500, 0)) - line_h_2 = Draft.make_line(Vector(1500, 500, 0), Vector(2000, 500, 0)) + line_1 = Draft.make_line(Vector(2000, 0, 0), Vector(2000, 500, 0)) + line_2 = Draft.make_line(Vector(2000, 500, 0), Vector(2500, 500, 0)) if App.GuiUp: - line_h_1.ViewObject.DrawStyle = "Dotted" - line_h_2.ViewObject.DrawStyle = "Dotted" + line_1.ViewObject.DrawStyle = "Dotted" + line_2.ViewObject.DrawStyle = "Dotted" doc.recompute() + Draft.make_fillet([line_1, line_2], 400) + _set_text(["Fillet"], Vector(2000, -200, 0)) - Draft.make_fillet([line_h_1, line_h_2], 400) - t_xpos += 900 - _set_text(["Fillet"], Vector(t_xpos, t_ypos, 0)) - - # Circle, arc, arc by 3 points - _msg(16 * "-") - _msg("Circle") - circle = Draft.make_circle(350) - circle.Placement.Base = Vector(2500, 500, 0) - t_xpos += 1050 - _set_text(["Circle"], Vector(t_xpos, t_ypos, 0)) - + # Circular arc _msg(16 * "-") _msg("Circular arc") - arc = Draft.make_circle(350, startangle=0, endangle=100) - arc.Placement.Base = Vector(3200, 500, 0) - t_xpos += 800 - _set_text(["Circular arc"], Vector(t_xpos, t_ypos, 0)) + arc = Draft.make_circle(250, startangle=90, endangle=270) + arc.Placement.Base = Vector(3250, 250, 0) + _set_text(["Circular arc"], Vector(3000, -200, 0)) + # Circular arc 3 points _msg(16 * "-") _msg("Circular arc 3 points") - Draft.make_arc_3points([Vector(4600, 0, 0), - Vector(4600, 800, 0), - Vector(4000, 1000, 0)]) - t_xpos += 600 - _set_text(["Circular arc 3 points"], Vector(t_xpos, t_ypos, 0)) + Draft.make_arc_3points([Vector(4250, 0, 0), + Vector(4000, 250, 0), + Vector(4250, 500, 0)]) + _set_text(["Circular arc 3 points"], Vector(4000, -200, 0)) - # Ellipse, polygon, rectangle + # Circle + _msg(16 * "-") + _msg("Circle") + circle = Draft.make_circle(250) + circle.Placement.Base = Vector(5250, 250, 0) + _set_text(["Circle"], Vector(5000, -200, 0)) + + # Ellipse _msg(16 * "-") _msg("Ellipse") - ellipse = Draft.make_ellipse(500, 300) - ellipse.Placement.Base = Vector(5500, 250, 0) - t_xpos += 1600 - _set_text(["Ellipse"], Vector(t_xpos, t_ypos, 0)) + ellipse = Draft.make_ellipse(250, 150) + ellipse.Placement.Base = Vector(6250, 150, 0) + _set_text(["Ellipse"], Vector(6000, -200, 0)) + # Rectangle + _msg(16 * "-") + _msg("Rectangle") + rectangle = Draft.make_rectangle(500, 300, 0) + rectangle.Placement.Base = Vector(7000, 0, 0) + _set_text(["Rectangle"], Vector(7000, -200, 0)) + + # Polygon _msg(16 * "-") _msg("Polygon") polygon = Draft.make_polygon(5, 250) - polygon.Placement.Base = Vector(6500, 500, 0) - t_xpos += 950 - _set_text(["Polygon"], Vector(t_xpos, t_ypos, 0)) - - _msg(16 * "-") - _msg("Rectangle") - rectangle = Draft.make_rectangle(500, 1000, 0) - rectangle.Placement.Base = Vector(7000, 0, 0) - t_xpos += 650 - _set_text(["Rectangle"], Vector(t_xpos, t_ypos, 0)) - - # Text - _msg(16 * "-") - _msg("Text") - text = Draft.make_text(["Testing", "text"], Vector(7700, 500, 0)) - if App.GuiUp: - text.ViewObject.FontSize = 100 - t_xpos += 700 - _set_text(["Text"], Vector(t_xpos, t_ypos, 0)) - - # Linear dimension - _msg(16 * "-") - _msg("Linear dimension") - line = Draft.make_wire([Vector(8700, 200, 0), - Vector(8700, 1200, 0)]) - - dimension = Draft.make_linear_dimension(Vector(8600, 200, 0), - Vector(8600, 1000, 0), - Vector(8400, 750, 0)) - if App.GuiUp: - dimension.ViewObject.ArrowSize = 15 - dimension.ViewObject.ExtLines = 1000 - dimension.ViewObject.ExtOvershoot = 100 - dimension.ViewObject.DimOvershoot = 50 - dimension.ViewObject.FontSize = 100 - dimension.ViewObject.ShowUnit = False - doc.recompute() - - dim_obj = Draft.make_linear_dimension_obj(line, 1, 2, - Vector(9000, 750, 0)) - if App.GuiUp: - dim_obj.ViewObject.ArrowSize = 15 - dim_obj.ViewObject.ArrowType = "Arrow" - dim_obj.ViewObject.ExtLines = 100 - dim_obj.ViewObject.ExtOvershoot = 100 - dim_obj.ViewObject.DimOvershoot = 50 - dim_obj.ViewObject.FontSize = 100 - dim_obj.ViewObject.ShowUnit = False - - t_xpos += 680 - _set_text(["Dimension"], Vector(t_xpos, t_ypos, 0)) - - # Radius and diameter dimension - _msg(16 * "-") - _msg("Radius and diameter dimension") - arc_h = Draft.make_circle(500, startangle=0, endangle=90) - arc_h.Placement.Base = Vector(9500, 0, 0) - doc.recompute() - - dimension_r = Draft.make_radial_dimension_obj(arc_h, 1, - "radius", - Vector(9750, 200, 0)) - if App.GuiUp: - dimension_r.ViewObject.ArrowSize = 15 - dimension_r.ViewObject.FontSize = 100 - dimension_r.ViewObject.ShowUnit = False - - arc_h2 = Draft.make_circle(450, startangle=-120, endangle=80) - arc_h2.Placement.Base = Vector(10000, 1000, 0) - doc.recompute() - - dimension_d = Draft.make_radial_dimension_obj(arc_h2, 1, - "diameter", - Vector(10750, 900, 0)) - if App.GuiUp: - dimension_d.ViewObject.ArrowSize = 15 - dimension_d.ViewObject.FontSize = 100 - dimension_d.ViewObject.ShowUnit = False - t_xpos += 950 - _set_text(["Radius dimension", - "Diameter dimension"], Vector(t_xpos, t_ypos, 0)) - - # Angular dimension - _msg(16 * "-") - _msg("Angular dimension") - Draft.make_line(Vector(10500, 300, 0), Vector(11500, 1000, 0)) - Draft.make_line(Vector(10500, 300, 0), Vector(11500, 0, 0)) - angle1 = -20 - angle2 = 40 - dimension_a = Draft.make_angular_dimension(Vector(10500, 300, 0), - [angle1, angle2], - Vector(11500, 300, 0)) - if App.GuiUp: - dimension_a.ViewObject.ArrowSize = 15 - dimension_a.ViewObject.FontSize = 100 - t_xpos += 1700 - _set_text(["Angle dimension"], Vector(t_xpos, t_ypos, 0)) + polygon.Placement.Base = Vector(8250, 250, 0) + _set_text(["Polygon"], Vector(8000, -200, 0)) # BSpline _msg(16 * "-") _msg("BSpline") - Draft.make_bspline([Vector(12500, 0, 0), - Vector(12500, 500, 0), - Vector(13000, 500, 0), - Vector(13000, 1000, 0)]) - t_xpos += 1500 - _set_text(["BSpline"], Vector(t_xpos, t_ypos, 0)) + Draft.make_bspline([Vector(9000, 0, 0), + Vector(9100, 200, 0), + Vector(9400, 300, 0), + Vector(9500, 500, 0)]) + _set_text(["BSpline"], Vector(9000, -200, 0)) + + # Cubic bezier + _msg(16 * "-") + _msg("Cubic bezier") + Draft.make_bezcurve([Vector(10000, 0, 0), + Vector(10000, 500, 0), + Vector(10500, 0, 0), + Vector(10500, 500, 0)], degree=3) + _set_text(["Cubic bezier"], Vector(10000, -200, 0)) + + # N-degree bezier + _msg(16 * "-") + _msg("N-degree bezier") + Draft.make_bezcurve([Vector (11000, 0, 0), + Vector (11100, 400, 0), + Vector (11250, 250, 0), + Vector (11400, 100, 0), + Vector (11500, 500, 0)]) + _set_text(["N-degree bezier"], Vector(11000, -200, 0)) # Point _msg(16 * "-") _msg("Point") - point = Draft.make_point(13500, 500, 0) + point = Draft.make_point(12000, 0, 0) if App.GuiUp: point.ViewObject.PointSize = 10 - t_xpos += 900 - _set_text(["Point"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Point"], Vector(12000, -200, 0)) + + # Facebinder + _msg(16 * "-") + _msg("Facebinder") + box = doc.addObject("Part::Box", "Box") + box.Length = 200 + box.Width = 500 + box.Height = 100 + box.Placement.Base = Vector(13000, 0, 0) + if App.GuiUp: + box.ViewObject.Visibility = False + facebinder = Draft.make_facebinder([(box, ("Face1", "Face3", "Face6"))]) + facebinder.Extrusion = 10 + _set_text(["Facebinder"], Vector(13000, -200, 0)) # Shapestring _msg(16 * "-") @@ -313,268 +239,347 @@ def _create_objects(doc=None, shape_string = Draft.make_shapestring("Testing", font_file, 100) - shape_string.Placement.Base = Vector(14000, 500) + shape_string.Placement.Base = Vector(14000, 0) except Exception: _wrn("Shapestring could not be created") _wrn("Possible cause: the font file may not exist") _wrn(font_file) - rect = Draft.make_rectangle(500, 100) - rect.Placement.Base = Vector(14000, 500) - t_xpos += 600 - _set_text(["Shapestring"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Shapestring"], Vector(14000, -200, 0)) - # Facebinder + # Hatch _msg(16 * "-") - _msg("Facebinder") - box = doc.addObject("Part::Box", "Cube") - box.Length = 200 - box.Width = 500 - box.Height = 100 - box.Placement.Base = Vector(15000, 0, 0) + _msg("Hatch") + rectangle = Draft.make_rectangle(500, 300, 0) + rectangle.Placement.Base = Vector(15000, 0, 0) + rectangle.MakeFace = True if App.GuiUp: - box.ViewObject.Visibility = False + rectangle.ViewObject.Visibility = False + try: + Draft.make_hatch(rectangle, + hatch_file, + hatch_name, + scale=10, + rotation=45) + except Exception: + _wrn("Hatch could not be created") + _wrn("Possible cause: the hatch file may not exist") + _wrn(hatch_file) + _set_text(["Hatch"], Vector(15000, -200, 0)) - facebinder = Draft.make_facebinder([(box, ("Face1", "Face3", "Face6"))]) - facebinder.Extrusion = 10 - t_xpos += 780 - _set_text(["Facebinder"], Vector(t_xpos, t_ypos, 0)) + # Annotation ############################################################ - # Cubic bezier curve, n-degree bezier curve + # Text _msg(16 * "-") - _msg("Cubic bezier") - Draft.make_bezcurve([Vector(15500, 0, 0), - Vector(15578, 485, 0), - Vector(15879, 154, 0), - Vector(15975, 400, 0), - Vector(16070, 668, 0), - Vector(16423, 925, 0), - Vector(16500, 500, 0)], degree=3) - t_xpos += 680 - _set_text(["Cubic bezier"], Vector(t_xpos, t_ypos, 0)) + _msg("Text") + text = Draft.make_text(["Testing", "text"], Vector(0, 2100, 0)) + if App.GuiUp: + text.ViewObject.FontSize = 100 + _set_text(["Text"], Vector(0, 1800, 0)) + # Linear dimension _msg(16 * "-") - _msg("N-degree bezier") - Draft.make_bezcurve([Vector(16500, 0, 0), - Vector(17000, 500, 0), - Vector(17500, 500, 0), - Vector(17500, 1000, 0), - Vector(17000, 1000, 0), - Vector(17063, 1256, 0), - Vector(17732, 1227, 0), - Vector(17790, 720, 0), - Vector(17702, 242, 0)]) - t_xpos += 1200 - _set_text(["n-Bezier"], Vector(t_xpos, t_ypos, 0)) + _msg("Linear dimension") + dimension = Draft.make_linear_dimension(Vector(1500, 2000, 0), + Vector(1500, 2400, 0), + Vector(1000, 2200, 0)) + if App.GuiUp: + dimension.ViewObject.ArrowSize = 15 + dimension.ViewObject.ExtLines = 0 + dimension.ViewObject.ExtOvershoot = 50 + dimension.ViewObject.DimOvershoot = 25 + dimension.ViewObject.FontSize = 50 + dimension.ViewObject.Decimals = 1 + dimension.ViewObject.ShowUnit = False + + line = Draft.make_wire([Vector(1500, 2600, 0), + Vector(1500, 3000, 0)]) + doc.recompute() + dimension = Draft.make_linear_dimension_obj(line, 1, 2, + Vector(1000, 2800, 0)) + if App.GuiUp: + dimension.ViewObject.ArrowSize = 15 + dimension.ViewObject.ArrowType = "Arrow" + dimension.ViewObject.ExtLines = -50 + dimension.ViewObject.ExtOvershoot = 50 + dimension.ViewObject.DimOvershoot = 25 + dimension.ViewObject.FontSize = 50 + dimension.ViewObject.Decimals = 1 + dimension.ViewObject.ShowUnit = False + + _set_text(["Dimension"], Vector(1000, 1800, 0)) + + # Radius and diameter dimension + _msg(16 * "-") + _msg("Radius and diameter dimension") + circle = Draft.make_circle(200) + circle.Placement.Base = Vector(2200, 2200, 0) + circle.MakeFace = False + doc.recompute() + dimension = Draft.make_radial_dimension_obj(circle, + 1, + "radius", + Vector(2300, 2300, 0)) + if App.GuiUp: + dimension.ViewObject.ArrowSize = 15 + dimension.ViewObject.FontSize = 50 + dimension.ViewObject.Decimals = 1 + dimension.ViewObject.ShowUnit = False + + circle = Draft.make_circle(200) + circle.Placement.Base = Vector(2200, 2800, 0) + circle.MakeFace = False + doc.recompute() + dimension = Draft.make_radial_dimension_obj(circle, + 1, + "diameter", + Vector(2300, 2900, 0)) + if App.GuiUp: + dimension.ViewObject.ArrowSize = 15 + dimension.ViewObject.FontSize = 50 + dimension.ViewObject.Decimals = 1 + dimension.ViewObject.ShowUnit = False + _set_text(["Radius dimension", + "Diameter dimension"], Vector(2000, 1800, 0)) + + # Angular dimension + _msg(16 * "-") + _msg("Angular dimension") + Draft.make_line(Vector(3000, 2000, 0), Vector(3500, 2000, 0)) + Draft.make_line(Vector(3000, 2000, 0), Vector(3500, 2500, 0)) + dimension = Draft.make_angular_dimension(Vector(3000, 2000, 0), + [0, 45], + Vector(3250, 2250, 0)) + if App.GuiUp: + dimension.ViewObject.ArrowSize = 15 + dimension.ViewObject.FontSize = 50 + dimension.ViewObject.Decimals = 1 + _set_text(["Angle dimension"], Vector(3000, 1800, 0)) # Label _msg(16 * "-") _msg("Label") - place = App.Placement(Vector(18500, 500, 0), App.Rotation()) - label = Draft.make_label(target_point=Vector(18000, 0, 0), + place = App.Placement(Vector(4250, 2250, 0), App.Rotation()) + label = Draft.make_label(target_point=Vector(4000, 2000, 0), placement=place, custom_text="Example label", - distance=-250) + distance=-100) label.Text = "Testing" if App.GuiUp: label.ViewObject.ArrowSize = 15 - label.ViewObject.TextSize = 100 + label.ViewObject.TextSize = 50 doc.recompute() - t_xpos += 1200 - _set_text(["Label"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Label"], Vector(4000, 1800, 0)) - # Orthogonal array and orthogonal link array + # Array ################################################################# + + # Orthogonal array _msg(16 * "-") _msg("Orthogonal array") - rect_h = Draft.make_rectangle(500, 500) - rect_h.Placement.Base = Vector(1500, 2500, 0) + rectangle = Draft.make_rectangle(100, 50) + rectangle.Placement.Base = Vector(0, 4000, 0) if App.GuiUp: - rect_h.ViewObject.Visibility = False - - Draft.make_ortho_array(rect_h, - Vector(600, 0, 0), - Vector(0, 600, 0), + rectangle.ViewObject.Visibility = False + Draft.make_ortho_array(rectangle, + Vector(200, 0, 0), + Vector(0, 150, 0), Vector(0, 0, 0), - 3, 2, 1, + 3, + 2, + 1, use_link=False) - t_xpos = 1700 - t_ypos = 2200 - _set_text(["Array"], Vector(t_xpos, t_ypos, 0)) - - rect_h_2 = Draft.make_rectangle(500, 100) - rect_h_2.Placement.Base = Vector(1500, 5000, 0) - if App.GuiUp: - rect_h_2.ViewObject.Visibility = False + _set_text(["Orthogonal array"], Vector(0, 3800, 0)) + # Orthogonal link array _msg(16 * "-") _msg("Orthogonal link array") - Draft.make_ortho_array(rect_h_2, - Vector(800, 0, 0), - Vector(0, 500, 0), + rectangle = Draft.make_rectangle(50, 50) + rectangle.Placement.Base = Vector(1000, 4000, 0) + if App.GuiUp: + rectangle.ViewObject.Visibility = False + Draft.make_ortho_array(rectangle, + Vector(200, 0, 0), + Vector(0, 150, 0), Vector(0, 0, 0), - 2, 4, 1, + 3, + 2, + 1, use_link=True) - t_ypos += 2600 - _set_text(["Link array"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Orthogonal link array"], Vector(1000, 3800, 0)) - # Polar array and polar link array + # Polar array _msg(16 * "-") _msg("Polar array") - wire_h = Draft.make_wire([Vector(5500, 3000, 0), - Vector(6000, 3500, 0), - Vector(6000, 3200, 0), - Vector(5800, 3200, 0)]) + wire = Draft.make_wire([Vector(2000, 4050, 0), + Vector(2000, 4000, 0), + Vector(2100, 4000, 0)]) if App.GuiUp: - wire_h.ViewObject.Visibility = False - - Draft.make_polar_array(wire_h, - 8, - 200, - Vector(5000, 3000, 0), + wire.ViewObject.Visibility = False + Draft.make_polar_array(wire, + 4, + 90, + Vector(2000, 4250, 0), use_link=False) + _set_text(["Polar array"], Vector(2000, 3800, 0)) - t_xpos = 4600 - t_ypos = 2200 - _set_text(["Polar array"], Vector(t_xpos, t_ypos, 0)) - + # Polar link array _msg(16 * "-") _msg("Polar link array") - wire_h_2 = Draft.make_wire([Vector(5500, 6000, 0), - Vector(6000, 6000, 0), - Vector(5800, 5700, 0), - Vector(5800, 5750, 0)]) + wire = Draft.make_wire([Vector(3000, 4050, 0), + Vector(3000, 4000, 0), + Vector(3050, 4000, 0)]) if App.GuiUp: - wire_h_2.ViewObject.Visibility = False - - Draft.make_polar_array(wire_h_2, - 8, - 200, - Vector(5000, 6000, 0), + wire.ViewObject.Visibility = False + Draft.make_polar_array(wire, + 4, + 90, + Vector(3000, 4250, 0), use_link=True) - t_ypos += 3200 - _set_text(["Polar link array"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Polar link array"], Vector(3000, 3800, 0)) - # Circular array and circular link array + # Circular array _msg(16 * "-") _msg("Circular array") - poly_h = Draft.make_polygon(5, 200) - poly_h.Placement.Base = Vector(8000, 3000, 0) + polygon = Draft.make_polygon(5, 30) + polygon.Placement.Base = Vector(4250, 4250, 0) if App.GuiUp: - poly_h.ViewObject.Visibility = False - - Draft.make_circular_array(poly_h, - 500, 600, + polygon.ViewObject.Visibility = False + Draft.make_circular_array(polygon, + 110, + 100, 3, 1, Vector(0, 0, 1), Vector(0, 0, 0), use_link=False) - t_xpos = 7700 - t_ypos = 1700 - _set_text(["Circular array"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Circular array"], Vector(4000, 3800, 0)) + # Circular link array _msg(16 * "-") _msg("Circular link array") - poly_h_2 = Draft.make_polygon(6, 150) - poly_h_2.Placement.Base = Vector(8000, 6250, 0) + polygon = Draft.make_polygon(6, 30) + polygon.Placement.Base = Vector(5250, 4250, 0) if App.GuiUp: - poly_h_2.ViewObject.Visibility = False - - Draft.make_circular_array(poly_h_2, - 550, 450, + polygon.ViewObject.Visibility = False + Draft.make_circular_array(polygon, + 110, + 100, 3, 1, Vector(0, 0, 1), Vector(0, 0, 0), use_link=True) - t_ypos += 3100 - _set_text(["Circular link array"], Vector(t_xpos, t_ypos, 0)) + _set_text(["Circular link array"], Vector(5000, 3800, 0)) - # Path array and path link array + # Path array _msg(16 * "-") _msg("Path array") - poly_h = Draft.make_polygon(3, 250) - poly_h.Placement.Base = Vector(10000, 3000, 0) + polygon = Draft.make_polygon(3, 30) + polygon.Placement.Base = Vector(6000, 4000, 0) if App.GuiUp: - poly_h.ViewObject.Visibility = False - - bspline_path = Draft.make_bspline([Vector(10500, 2500, 0), - Vector(11000, 3000, 0), - Vector(11500, 3200, 0), - Vector(12000, 4000, 0)]) - - Draft.make_path_array(poly_h, bspline_path, 5, - use_link=False) - t_xpos = 10400 - t_ypos = 2200 - _set_text(["Path array"], Vector(t_xpos, t_ypos, 0)) + polygon.ViewObject.Visibility = False + spline = Draft.make_bspline([Vector(6000, 4000, 0), + Vector(6100, 4200, 0), + Vector(6400, 4300, 0), + Vector(6500, 4500, 0)]) + Draft.make_path_array(polygon, spline, 5, use_link=False) + _set_text(["Path array"], Vector(6000, 3800, 0)) + # Path link array _msg(16 * "-") _msg("Path link array") - poly_h_2 = Draft.make_polygon(4, 200) - poly_h_2.Placement.Base = Vector(10000, 5000, 0) + polygon = Draft.make_polygon(4, 30) + polygon.Placement.Base = Vector(7000, 4000, 0) if App.GuiUp: - poly_h_2.ViewObject.Visibility = False - - bspline_path_2 = Draft.make_bspline([Vector(10500, 4500, 0), - Vector(11000, 6800, 0), - Vector(11500, 6000, 0), - Vector(12000, 5200, 0)]) - - Draft.make_path_array(poly_h_2, bspline_path_2, 6, - use_link=True) - t_ypos += 2000 - _set_text(["Path link array"], Vector(t_xpos, t_ypos, 0)) + polygon.ViewObject.Visibility = False + spline = Draft.make_bspline([Vector(7000, 4000, 0), + Vector(7100, 4200, 0), + Vector(7400, 4300, 0), + Vector(7500, 4500, 0)]) + Draft.make_path_array(polygon, spline, 5, use_link=True) + _set_text(["Path link array"], Vector(7000, 3800, 0)) # Point array _msg(16 * "-") _msg("Point array") - poly_h = Draft.make_polygon(3, 250) - poly_h.Placement.Base = Vector(12500, 2500, 0) - - point_1 = Draft.make_point(13000, 3000, 0) - point_2 = Draft.make_point(13000, 3500, 0) - point_3 = Draft.make_point(14000, 2500, 0) - point_4 = Draft.make_point(14000, 3000, 0) - + polygon = Draft.make_polygon(3, 30) + polygon.Placement.Base = Vector(8000, 4000, 0) + point_1 = Draft.make_point(8030, 4030, 0) + point_2 = Draft.make_point(8030, 4250, 0) + point_3 = Draft.make_point(8470, 4250, 0) + point_4 = Draft.make_point(8470, 4470, 0) add_list, delete_list = Draft.upgrade([point_1, point_2, point_3, point_4]) compound = add_list[0] if App.GuiUp: compound.ViewObject.PointSize = 5 + Draft.make_point_array(polygon, compound, use_link=False) + _set_text(["Point array"], Vector(8000, 3800, 0)) - Draft.make_point_array(poly_h, compound) - t_xpos = 13000 - t_ypos = 2200 - _set_text(["Point array"], Vector(t_xpos, t_ypos, 0)) - - # Clone and mirror + # Point link array _msg(16 * "-") - _msg("Clone") - wire_h = Draft.make_wire([Vector(15000, 2500, 0), - Vector(15200, 3000, 0), - Vector(15500, 2500, 0), - Vector(15200, 2300, 0)]) + _msg("Point link array") + polygon = Draft.make_polygon(4, 30) + polygon.Placement.Base = Vector(9000, 4000, 0) + point_1 = Draft.make_point(9030, 4030, 0) + point_2 = Draft.make_point(9030, 4250, 0) + point_3 = Draft.make_point(9470, 4250, 0) + point_4 = Draft.make_point(9470, 4470, 0) + add_list, delete_list = Draft.upgrade([point_1, point_2, + point_3, point_4]) + compound = add_list[0] + if App.GuiUp: + compound.ViewObject.PointSize = 5 + Draft.make_point_array(polygon, compound, use_link=True) + _set_text(["Point link array"], Vector(9000, 3800, 0)) - Draft.make_clone(wire_h, Vector(0, 1000, 0)) - t_xpos = 15000 - t_ypos = 2100 - _set_text(["Clone"], Vector(t_xpos, t_ypos, 0)) + # Miscellaneous ######################################################### + # Mirror _msg(16 * "-") _msg("Mirror") - wire_h = Draft.make_wire([Vector(17000, 2500, 0), - Vector(16500, 4000, 0), - Vector(16000, 2700, 0), - Vector(16500, 2500, 0), - Vector(16700, 2700, 0)]) + wire = Draft.make_wire([Vector(0, 6000, 0), + Vector(150, 6200, 0), + Vector(500, 6000, 0)]) + Draft.mirror(wire, + Vector(0, 6250, 0), + Vector(500, 6250, 0)) + _set_text(["Mirror"], Vector(0, 5800, 0)) - Draft.mirror(wire_h, - Vector(17100, 2000, 0), - Vector(17100, 4000, 0)) - t_xpos = 17000 - t_ypos = 2200 - _set_text(["Mirror"], Vector(t_xpos, t_ypos, 0)) + # Clone + _msg(16 * "-") + _msg("Clone") + wire = Draft.make_wire([Vector(1000, 6000, 0), + Vector(1150, 6200, 0), + Vector(1500, 6000, 0)]) + Draft.make_clone(wire, Vector(0, 300, 0)) + _set_text(["Clone"], Vector(1000, 5800, 0)) + # Shape2DView + _msg(16 * "-") + _msg("Shape2DView") + place = App.Placement(Vector(2000, 6000, 0), + App.Rotation(Vector(0, 0, 1), Vector(1, 2, 3))) + box = doc.addObject("Part::Box", "Box") + box.Length = 200 + box.Width = 500 + box.Height = 100 + box.Placement = place + if App.GuiUp: + box.ViewObject.Visibility = False + Draft.make_shape2dview(box) + _set_text(["Shape2DView"], Vector(2000, 5800, 0)) + + # WorkingPlaneProxy + _msg(16 * "-") + _msg("WorkingPlaneProxy") + place = App.Placement(Vector(3250, 6250, 0), App.Rotation()) + proxy = Draft.make_workingplaneproxy(place) + if App.GuiUp: + proxy.ViewObject.DisplaySize = 500 + proxy.ViewObject.ArrowSize = 50 + _set_text(["WorkingPlaneProxy"], Vector(3000, 5800, 0)) + + # Layer _msg(16 * "-") _msg("Layer") layer = Draft.make_layer("Custom layer", @@ -582,39 +587,24 @@ def _create_objects(doc=None, shape_color=(0.56, 0.89, 0.56), line_width=4, transparency=50) - cube = doc.addObject('Part::Box') - cube.Length = 350 - cube.Width = 300 - cube.Height = 250 - cube.Placement.Base = Vector(14000, 5500, 0) - - cone = doc.addObject('Part::Cone') - cone.Radius1 = 400 - cone.Height = 600 - cone.Angle = 270 - cone.Placement.Base = Vector(15000, 6000, 0) - - sphere = doc.addObject('Part::Sphere') - sphere.Radius = 450 - sphere.Angle1 = -45 - sphere.Angle2 = 45 - sphere.Angle3 = 300 - sphere.Placement.Base = Vector(14000, 7000, 0) - - layer.Proxy.addObject(layer, cube) - layer.Proxy.addObject(layer, cone) + box = doc.addObject("Part::Box", "Box") + box.Length = 200 + box.Width = 500 + box.Height = 100 + box.Placement.Base = Vector(4000, 6000, 0) + sphere = doc.addObject("Part::Sphere", "Sphere") + sphere.Radius = 100 + sphere.Placement.Base = Vector(4400, 6250, 0) + layer.Proxy.addObject(layer, box) layer.Proxy.addObject(layer, sphere) + _set_text(["Layer"], Vector(4000, 5800, 0)) - t_xpos = 14000 - t_ypos = 5000 - _set_text(["Layer"], Vector(t_xpos, t_ypos, 0)) doc.recompute() -def create_test_file(file_name="draft_test_objects", - file_path=os.environ["HOME"], - save=False, - font_file="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"): +def create_test_file(font_file=App.getHomePath()+"data/Mod/TechDraw/Resources/fonts/osifont-lgpl3fe.ttf", + hatch_file=App.getHomePath()+"data/Mod/TechDraw/PAT/FCPAT.pat", + hatch_name="Horizontal5"): """Create a complete test file of Draft objects. It draws a frame with information on the software used to create @@ -622,63 +612,35 @@ def create_test_file(file_name="draft_test_objects", Parameters ---------- - file_name: str, optional - It defaults to `'draft_test_objects'`. - It is the name of the document that is created. - - The `file_name` will be appended to `file_path` - to determine the actual path to save. The extension `.FCStd` - will be added automatically. - - file_path: str, optional - It defaults to the value of `os.environ['HOME']` - which in Linux is usually `'/home/user'`. - - If it is the empty string `''` it will use the value - returned by `App.getUserAppDataDir()`, - for example, `'/home/user/.FreeCAD/'`. - - save: bool, optional - It defaults to `False`. If it is `True` the new document - will be saved to disk after creating all objects. - font_file: str, optional - It defaults to `'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf'`. - It is the full path of a font in the system to be used - to create a `Draft ShapeString`. - If the font is not found, this object is not created. + It defaults to `App.getHomePath()+"data/Mod/TechDraw/Resources/fonts/osifont-lgpl3fe.ttf"` + It is the full path of a font file to be used to create a `Draft ShapeString`. + If the file is not found, this object is not created. + + hatch_file: str, optional + It defaults to `App.getHomePath()+"data/Mod/TechDraw/PAT/FCPAT.pat"` + It is the full path of a PAT file to be used to create a `Draft Hatch`. + If the file is not found, this object is not created. + + hatch_name: str, optional + It defaults to `"Horizontal5"` + It is the name of a hatch pattern in the hatch_file. Returns ------- App::Document A reference to the test document that was created. - - To Do - ----- - Find a reliable way of getting a default font to be able to create - the `Draft ShapeString`. """ - doc = App.newDocument(file_name) _msg(16 * "-") - _msg("Filename: {}".format(file_name)) _msg("If the units tests fail, this script may fail as well") + doc = App.newDocument() _create_frame(doc=doc) - _create_objects(doc=doc, font_file=font_file) + _create_objects(doc=doc, font_file=font_file, hatch_file=hatch_file, hatch_name=hatch_name) if App.GuiUp: Gui.runCommand("Std_ViewFitAll") - - # Export - if not file_path: - file_path = App.getUserAppDataDir() - - out_name = os.path.join(file_path, file_name + ".FCStd") - doc.FileName = out_name - if save: - doc.save() - _msg(16 * "-") - _msg("Saved: {}".format(out_name)) + Gui.Selection.clearSelection() return doc diff --git a/src/Mod/MeshPart/App/MeshFlattening.cpp b/src/Mod/MeshPart/App/MeshFlattening.cpp index 245ed98b12..d01f8496c1 100644 --- a/src/Mod/MeshPart/App/MeshFlattening.cpp +++ b/src/Mod/MeshPart/App/MeshFlattening.cpp @@ -141,37 +141,37 @@ FaceUnwrapper::FaceUnwrapper(const TopoDS_Face& face) if (triangulation.IsNull()) throw std::runtime_error("null triangulation in face construction"); + Standard_Integer numNodes = triangulation->NbNodes(); + Standard_Integer numTriangles = triangulation->NbTriangles(); + // compute uv coordinates if (triangulation->HasUVNodes()) { - const TColgp_Array1OfPnt2d &_uv_nodes = triangulation->UVNodes(); - this->uv_nodes.resize(triangulation->NbNodes(), 2); + this->uv_nodes.resize(numNodes, 2); i = 0; - for (Standard_Integer index = _uv_nodes.Lower(); index <= _uv_nodes.Upper(); ++index) + for (Standard_Integer index = 1; index <= numNodes; ++index) { - const gp_Pnt2d& _uv_node = _uv_nodes.Value(index); + const gp_Pnt2d& _uv_node = triangulation->UVNode(index); this->uv_nodes.row(i) << _uv_node.X(), _uv_node.Y(); i++; } } // - const TColgp_Array1OfPnt &_nodes = triangulation->Nodes(); - this->xyz_nodes.resize(triangulation->NbNodes(), 3); + this->xyz_nodes.resize(numNodes, 3); i = 0; - for (Standard_Integer index = _nodes.Lower(); index <= _nodes.Upper(); ++index) + for (Standard_Integer index = 1; index <= numNodes; ++index) { - gp_Pnt _node = _nodes.Value(index); + gp_Pnt _node = triangulation->Node(index); this->xyz_nodes.row(i) << _node.X(), _node.Y(), _node.Z(); i++; } - - const Poly_Array1OfTriangle &_tris = triangulation->Triangles(); - this->tris.resize(triangulation->NbTriangles(), 3); + + this->tris.resize(numTriangles, 3); i = 0; - for (Standard_Integer index = _tris.Lower(); index <= _tris.Upper(); ++index) + for (Standard_Integer index = 1; index <= numTriangles; ++index) { int n1, n2, n3; - Poly_Triangle _tri = _tris.Value(index); + const Poly_Triangle& _tri = triangulation->Triangle(index); _tri.Get(n1, n2, n3); this->tris.row(i) << n1-1, n2-1, n3-1; i++; diff --git a/src/Mod/Part/AttachmentEditor/Commands.py b/src/Mod/Part/AttachmentEditor/Commands.py index f19e7c813c..3a477f995c 100644 --- a/src/Mod/Part/AttachmentEditor/Commands.py +++ b/src/Mod/Part/AttachmentEditor/Commands.py @@ -99,7 +99,6 @@ class CommandEditAttachment: return False if App.GuiUp: - global command_instance import FreeCADGui as Gui command_instance = CommandEditAttachment() Gui.addCommand('Part_EditAttachment', command_instance) diff --git a/src/Mod/Part/Gui/TaskFaceColors.cpp b/src/Mod/Part/Gui/TaskFaceColors.cpp index d7195a65be..be1126d48f 100644 --- a/src/Mod/Part/Gui/TaskFaceColors.cpp +++ b/src/Mod/Part/Gui/TaskFaceColors.cpp @@ -272,6 +272,7 @@ FaceColors::FaceColors(ViewProviderPartExt* vp, QWidget* parent) d->ui->setupUi(this); d->ui->groupBox->setTitle(QString::fromUtf8(vp->getObject()->Label.getValue())); d->ui->colorButton->setDisabled(true); + d->ui->colorButton->setAllowTransparency(true); FaceSelection* gate = new FaceSelection(d->vp->getObject()); Gui::Selection().addSelectionGate(gate); @@ -354,10 +355,9 @@ void FaceColors::on_defaultButton_clicked() void FaceColors::on_colorButton_changed() { if (!d->index.isEmpty()) { - float alpha = static_cast(d->vp->Transparency.getValue())/100; QColor c = d->ui->colorButton->color(); for (QSet::iterator it = d->index.begin(); it != d->index.end(); ++it) { - d->perface[*it].set(c.redF(), c.greenF(), c.blueF(), alpha); + d->perface[*it].set(c.redF(), c.greenF(), c.blueF(), c.alphaF()); } d->vp->DiffuseColor.setValues(d->perface); } @@ -380,7 +380,7 @@ void FaceColors::onSelectionChanged(const Gui::SelectionChanges& msg) d->index.insert(index); const App::Color& c = d->perface[index]; QColor color; - color.setRgbF(c.r,c.g,c.b); + color.setRgbF(c.r,c.g,c.b,c.a); d->ui->colorButton->setColor(color); selection_changed = true; } diff --git a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp index d0db2dfb97..9b41a6637d 100644 --- a/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp +++ b/src/Mod/Sketcher/Gui/CommandSketcherTools.cpp @@ -1292,6 +1292,7 @@ public: Sketcher::PointPos originpos, int nelements, SketcherCopy::Op op) : Mode(STATUS_SEEK_First) + , snapMode(SnapMode::Free) , geoIdList(geoidlist) , Origin() , OriginGeoId(origingeoid) @@ -1309,6 +1310,11 @@ public: STATUS_End }; + enum class SnapMode { + Free, + Snap5Degree + }; + virtual void activated(ViewProviderSketch *sketchgui) { setCursor(QPixmap(cursor_createcopy), 7, 7); @@ -1319,15 +1325,29 @@ public: virtual void mouseMove(Base::Vector2d onSketchPos) { if (Mode == STATUS_SEEK_First) { + + if(QApplication::keyboardModifiers() == Qt::ControlModifier) + snapMode = SnapMode::Snap5Degree; + else + snapMode = SnapMode::Free; + float length = (onSketchPos - EditCurve[0]).Length(); - float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2d(1.f,0.f)); + float angle = (onSketchPos - EditCurve[0]).Angle(); + + Base::Vector2d endpoint = onSketchPos; + + if (snapMode == SnapMode::Snap5Degree) { + angle = round(angle / (M_PI/36)) * M_PI/36; + endpoint = EditCurve[0] + length * Base::Vector2d(cos(angle),sin(angle)); + } + SbString text; text.sprintf(" (%.1f, %.1fdeg)", length, angle * 180 / M_PI); - setPositionText(onSketchPos, text); + setPositionText(endpoint, text); - EditCurve[1] = onSketchPos; + EditCurve[1] = endpoint; sketchgui->drawEdit(EditCurve); - if (seekAutoConstraint(sugConstr1, onSketchPos, Base::Vector2d(0.0, 0.0), AutoConstraint::VERTEX)) { + if (seekAutoConstraint(sugConstr1, endpoint, Base::Vector2d(0.0, 0.0), AutoConstraint::VERTEX)) { renderSuggestConstraintsCursor(sugConstr1); return; } @@ -1335,10 +1355,9 @@ public: applyCursor(); } - virtual bool pressButton(Base::Vector2d onSketchPos) + virtual bool pressButton(Base::Vector2d) { if (Mode == STATUS_SEEK_First) { - EditCurve[1] = onSketchPos; sketchgui->drawEdit(EditCurve); Mode = STATUS_End; } @@ -1401,6 +1420,7 @@ public: } protected: SelectMode Mode; + SnapMode snapMode; string geoIdList; Base::Vector3d Origin; int OriginGeoId; @@ -1841,7 +1861,7 @@ public: enum class SnapMode { Free, - Snap10Degree + Snap5Degree }; virtual void activated(ViewProviderSketch *sketchgui) @@ -1856,17 +1876,17 @@ public: if (Mode==STATUS_SEEK_First) { if(QApplication::keyboardModifiers() == Qt::ControlModifier) - snapMode = SnapMode::Snap10Degree; + snapMode = SnapMode::Snap5Degree; else snapMode = SnapMode::Free; float length = (onSketchPos - EditCurve[0]).Length(); - float angle = (onSketchPos - EditCurve[0]).GetAngle(Base::Vector2d(1.f, 0.f)); + float angle = (onSketchPos - EditCurve[0]).Angle(); Base::Vector2d endpoint = onSketchPos; - if (snapMode == SnapMode::Snap10Degree) { - angle = round(angle / (M_PI/18)) * M_PI/18; + if (snapMode == SnapMode::Snap5Degree) { + angle = round(angle / (M_PI/36)) * M_PI/36; endpoint = EditCurve[0] + length * Base::Vector2d(cos(angle),sin(angle)); } diff --git a/src/Mod/Spreadsheet/App/PropertySheet.cpp b/src/Mod/Spreadsheet/App/PropertySheet.cpp index 8a6fa6542d..286b72890f 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.cpp +++ b/src/Mod/Spreadsheet/App/PropertySheet.cpp @@ -982,6 +982,14 @@ void PropertySheet::getSpans(CellAddress address, int & rows, int & cols) const } } +App::CellAddress Spreadsheet::PropertySheet::getAnchor(App::CellAddress address) const +{ + if (auto anchor = mergedCells.find(address); anchor != mergedCells.end()) + return anchor->second; + else + return address; +} + bool PropertySheet::isMergedCell(CellAddress address) const { return mergedCells.find(address) != mergedCells.end(); diff --git a/src/Mod/Spreadsheet/App/PropertySheet.h b/src/Mod/Spreadsheet/App/PropertySheet.h index 7e20cf6a7d..60694b85b5 100644 --- a/src/Mod/Spreadsheet/App/PropertySheet.h +++ b/src/Mod/Spreadsheet/App/PropertySheet.h @@ -148,6 +148,8 @@ public: void getSpans(App::CellAddress address, int &rows, int &cols) const; + App::CellAddress getAnchor(App::CellAddress address) const; + bool isMergedCell(App::CellAddress address) const; bool isHidden(App::CellAddress address) const; diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp index 3442e5a9b7..561e88c303 100644 --- a/src/Mod/Spreadsheet/App/Sheet.cpp +++ b/src/Mod/Spreadsheet/App/Sheet.cpp @@ -1063,6 +1063,11 @@ bool Sheet::isMergedCell(CellAddress address) const return cells.isMergedCell(address); } +App::CellAddress Spreadsheet::Sheet::getAnchor(App::CellAddress address) const +{ + return cells.getAnchor(address); +} + /** * @brief Set column with of column \a col to \a width- * @param col Index of column. diff --git a/src/Mod/Spreadsheet/App/Sheet.h b/src/Mod/Spreadsheet/App/Sheet.h index 7b60256885..18e898b849 100644 --- a/src/Mod/Spreadsheet/App/Sheet.h +++ b/src/Mod/Spreadsheet/App/Sheet.h @@ -110,6 +110,8 @@ public: bool isMergedCell(App::CellAddress address) const; + App::CellAddress getAnchor(App::CellAddress address) const; + void setColumnWidth(int col, int width); int getColumnWidth(int col) const; diff --git a/src/Mod/Spreadsheet/Gui/LineEdit.cpp b/src/Mod/Spreadsheet/Gui/LineEdit.cpp index 2e6a00a2c6..f5b5f5b7d6 100644 --- a/src/Mod/Spreadsheet/Gui/LineEdit.cpp +++ b/src/Mod/Spreadsheet/Gui/LineEdit.cpp @@ -26,68 +26,58 @@ # include #endif +#include + #include "LineEdit.h" using namespace SpreadsheetGui; LineEdit::LineEdit(QWidget *parent) : Gui::ExpressionLineEdit(parent, false, true) - , current() - , deltaCol(0) - , deltaRow(0) + , lastKeyPressed(0) { + setFocusPolicy(Qt::FocusPolicy::ClickFocus); +} + +bool LineEdit::eventFilter(QObject* object, QEvent* event) +{ + Q_UNUSED(object); + if (event && event->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(event); + if (keyEvent->key() == Qt::Key_Tab) { + // Special tab handling -- must be done via a QApplication event filter, otherwise the widget + // system will always grab the tab events + if (completerActive()) { + hideCompleter(); + event->accept(); + return true; // To make sure this tab press doesn't do anything else + } + else { + lastKeyPressed = keyEvent->key(); + lastModifiers = keyEvent->modifiers(); + } + } + } + return false; // We don't usually actually "handle" the tab event, we just keep track of it } bool LineEdit::event(QEvent *event) { - if (event && event->type() == QEvent::KeyPress) { + if (event && event->type() == QEvent::FocusIn) { + qApp->installEventFilter(this); + } + else if (event && event->type() == QEvent::FocusOut) { + qApp->removeEventFilter(this); + if (lastKeyPressed) + Q_EMIT finishedWithKey(lastKeyPressed, lastModifiers); + lastKeyPressed = 0; + } + else if (event && event->type() == QEvent::KeyPress && !completerActive()) { QKeyEvent * kevent = static_cast(event); - - if (kevent->key() == Qt::Key_Tab) { - if (kevent->modifiers() == 0) { - deltaCol = 1; - deltaRow = 0; - Q_EMIT returnPressed(); - return true; - } - } - else if (kevent->key() == Qt::Key_Backtab) { - if (kevent->modifiers() == Qt::ShiftModifier) { - deltaCol = -1; - deltaRow = 0; - Q_EMIT returnPressed(); - return true; - } - } - else if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) { - if (kevent->modifiers() == 0) { - deltaCol = 0; - deltaRow = 1; - Q_EMIT returnPressed(); - return true; - } - else if (kevent->modifiers() == Qt::ShiftModifier) { - deltaCol = 0; - deltaRow = -1; - Q_EMIT returnPressed(); - return true; - } - } + lastKeyPressed = kevent->key(); + lastModifiers = kevent->modifiers(); } return Gui::ExpressionLineEdit::event(event); } -void LineEdit::setIndex(QModelIndex _current) -{ - current = _current; -} - -QModelIndex LineEdit::next() const -{ - const QAbstractItemModel * m = current.model(); - - return m->index(qMin(qMax(0, current.row() + deltaRow), m->rowCount() - 1 ), - qMin(qMax(0, current.column() + deltaCol), m->columnCount() - 1 ) ); -} - #include "moc_LineEdit.cpp" diff --git a/src/Mod/Spreadsheet/Gui/LineEdit.h b/src/Mod/Spreadsheet/Gui/LineEdit.h index fa4e85185f..467e81cb7b 100644 --- a/src/Mod/Spreadsheet/Gui/LineEdit.h +++ b/src/Mod/Spreadsheet/Gui/LineEdit.h @@ -36,13 +36,17 @@ public: explicit LineEdit(QWidget *parent = 0); bool event(QEvent *event); - void setIndex(QModelIndex _current); - QModelIndex next() const; + +Q_SIGNALS: + void finishedWithKey(int key, Qt::KeyboardModifiers modifiers); private: - QModelIndex current; - int deltaCol; - int deltaRow; + bool eventFilter(QObject* object, QEvent* event); + + +private: + int lastKeyPressed; + Qt::KeyboardModifiers lastModifiers; }; } diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp index 11da87b0b5..86b532213e 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.cpp +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.cpp @@ -101,6 +101,7 @@ static std::pair selectedMinMaxColumns(QModelIndexList list) SheetTableView::SheetTableView(QWidget *parent) : QTableView(parent) , sheet(0) + , tabCounter(0) { setHorizontalHeader(new SheetViewHeader(this,Qt::Horizontal)); setVerticalHeader(new SheetViewHeader(this,Qt::Vertical)); @@ -319,7 +320,7 @@ void SheetTableView::insertColumnsAfter() { assert(sheet != 0); const auto columns = selectionModel()->selectedColumns(); - const auto & [min, max] = selectedMinMaxColumns(columns); + const auto& [min, max] = selectedMinMaxColumns(columns); assert(max - min == columns.size() - 1); Q_UNUSED(min) @@ -345,7 +346,7 @@ void SheetTableView::removeColumns() Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Remove rows")); for (std::vector::const_iterator it = sortedColumns.begin(); it != sortedColumns.end(); ++it) Gui::cmdAppObjectArgs(sheet, "removeColumns('%s', %d)", - columnName(*it).c_str(), 1); + columnName(*it).c_str(), 1); Gui::Command::commitCommand(); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.recompute()"); } @@ -365,7 +366,7 @@ void SheetTableView::updateCellSpan(CellAddress address) setSpan(address.row(), address.col(), rows, cols); } -void SheetTableView::setSheet(Sheet * _sheet) +void SheetTableView::setSheet(Sheet* _sheet) { sheet = _sheet; cellSpanChangedConnection = sheet->cellSpanChanged.connect(bind(&SheetTableView::updateCellSpan, this, bp::_1)); @@ -399,57 +400,45 @@ void SheetTableView::setSheet(Sheet * _sheet) } -void SheetTableView::commitData ( QWidget * editor ) +void SheetTableView::commitData(QWidget* editor) { QTableView::commitData(editor); } -bool SheetTableView::edit ( const QModelIndex & index, EditTrigger trigger, QEvent * event ) +bool SheetTableView::edit(const QModelIndex& index, EditTrigger trigger, QEvent* event) { - if (trigger & (QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed) ) + if (trigger & (QAbstractItemView::DoubleClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed)) currentEditIndex = index; return QTableView::edit(index, trigger, event); } -bool SheetTableView::event(QEvent *event) +bool SheetTableView::event(QEvent* event) { - /* Catch key presses for navigating the table; Enter/Return (+Shift), and Tab (+Shift) */ - if (event && event->type() == QEvent::KeyPress) { - QKeyEvent * kevent = static_cast(event); - - if (kevent->key() == Qt::Key_Tab) { - QModelIndex c = currentIndex(); - - if (kevent->modifiers() == 0) { - setCurrentIndex(model()->index(c.row(), qMin(c.column() + 1, model()->columnCount() -1))); - return true; - } - } - else if (kevent->key() == Qt::Key_Backtab) { - QModelIndex c = currentIndex(); - - if (kevent->modifiers() == Qt::ShiftModifier) { - setCurrentIndex(model()->index(c.row(), qMax(c.column() - 1, 0))); - return true; - } - } - else if (kevent->key() == Qt::Key_Enter || kevent->key() == Qt::Key_Return) { - QModelIndex c = currentIndex(); - - if (kevent->modifiers() == 0) { - setCurrentIndex(model()->index(qMin(c.row() + 1, model()->rowCount() - 1), c.column())); - return true; - } - else if (kevent->modifiers() == Qt::ShiftModifier) { - setCurrentIndex(model()->index(qMax(c.row() - 1, 0), c.column())); - return true; - } - } - else if (kevent->key() == Qt::Key_Delete) { + if (event && event->type() == QEvent::KeyPress && this->hasFocus()) { + // If this widget has focus, look for keyboard events that represent movement shortcuts + // and handle them. + QKeyEvent* kevent = static_cast(event); + switch (kevent->key()) { + case Qt::Key_Return: [[fallthrough]]; + case Qt::Key_Enter: [[fallthrough]]; + case Qt::Key_Home: [[fallthrough]]; + case Qt::Key_End: [[fallthrough]]; + case Qt::Key_Left: [[fallthrough]]; + case Qt::Key_Right: [[fallthrough]]; + case Qt::Key_Up: [[fallthrough]]; + case Qt::Key_Down: [[fallthrough]]; + case Qt::Key_Tab: [[fallthrough]]; + case Qt::Key_Backtab: + finishEditWithMove(kevent->key(), kevent->modifiers(), true); + return true; + // Also handle the delete key here: + case Qt::Key_Delete: deleteSelection(); return true; + default: + break; } - else if (kevent->matches(QKeySequence::Cut)) { + if (kevent->matches(QKeySequence::Cut)) { cutSelection(); return true; } @@ -468,18 +457,19 @@ bool SheetTableView::event(QEvent *event) kevent->modifiers() == Qt::ShiftModifier || kevent->modifiers() == Qt::KeypadModifier) { switch (kevent->key()) { - case Qt::Key_Return: - case Qt::Key_Enter: - case Qt::Key_Delete: - case Qt::Key_Home: - case Qt::Key_End: - case Qt::Key_Backspace: - case Qt::Key_Left: - case Qt::Key_Right: - case Qt::Key_Up: - case Qt::Key_Down: + case Qt::Key_Return: [[fallthrough]]; + case Qt::Key_Enter: [[fallthrough]]; + case Qt::Key_Delete: [[fallthrough]]; + case Qt::Key_Home: [[fallthrough]]; + case Qt::Key_End: [[fallthrough]]; + case Qt::Key_Backspace: [[fallthrough]]; + case Qt::Key_Left: [[fallthrough]]; + case Qt::Key_Right: [[fallthrough]]; + case Qt::Key_Up: [[fallthrough]]; + case Qt::Key_Down: [[fallthrough]]; case Qt::Key_Tab: kevent->accept(); + break; default: break; } @@ -614,13 +604,239 @@ void SheetTableView::pasteClipboard() } } -void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint) +void SheetTableView::finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion) { - SpreadsheetGui::LineEdit * le = qobject_cast(editor); + // A utility lambda for finding the beginning and ending of data regions + auto scanForRegionBoundary = [this](int& r, int& c, int dr, int dc) { + auto startAddress = CellAddress(r, c); + auto startCell = sheet->getCell(startAddress); + bool startedAtEmptyCell = startCell ? !startCell->isUsed() : true; + const int maxRow = this->model()->rowCount() - 1; + const int maxCol = this->model()->columnCount() - 1; + while (c + dc >= 0 && r + dr >= 0 && c + dc <= maxCol && r + dr <= maxRow) { + r += dr; + c += dc; + auto cell = sheet->getCell(CellAddress(r, c)); + auto cellIsEmpty = cell ? !cell->isUsed() : true; + if (cellIsEmpty && !startedAtEmptyCell) { + // Don't stop at the empty cell, stop at the last non-empty cell + r -= dr; + c -= dc; + break; + } + else if (!cellIsEmpty && startedAtEmptyCell) { + break; + } + } + if (r == startAddress.row() && c == startAddress.col()) { + // Always move at least one cell: + r += dr; + c += dc; + } + r = std::max(0, std::min(r, maxRow)); + c = std::max(0, std::min(c, maxCol)); + }; - currentEditIndex = QModelIndex(); + int targetRow = currentIndex().row(); + int targetColumn = currentIndex().column(); + int colSpan; + int rowSpan; + sheet->getSpans(CellAddress(targetRow, targetColumn), rowSpan, colSpan); + switch (keyPressed) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (modifiers == Qt::NoModifier) { + targetRow += rowSpan; + targetColumn -= tabCounter; + } + else if (modifiers == Qt::ShiftModifier) { + targetRow -= 1; + targetColumn -= tabCounter; + } + else { + // For an unrecognized modifier, just go down + targetRow += rowSpan; + } + tabCounter = 0; + break; + + case Qt::Key_Home: + // Home: row 1, same column + // Ctrl-Home: row 1, column 1 + targetRow = 0; + if (modifiers == Qt::ControlModifier) + targetColumn = 0; + tabCounter = 0; + break; + + case Qt::Key_End: + { + // End should take you to the last occupied cell in the current column + // Ctrl-End takes you to the last cell in the sheet + auto usedCells = sheet->getCells()->getUsedCells(); + for (const auto& cell : usedCells) { + if (modifiers == Qt::NoModifier) { + if (cell.col() == targetColumn) + targetRow = std::max(targetRow, cell.row()); + } + else if (modifiers == Qt::ControlModifier) { + targetRow = std::max(targetRow, cell.row()); + targetColumn = std::max(targetColumn, cell.col()); + } + } + tabCounter = 0; + break; + } + + case Qt::Key_Left: + if (targetColumn == 0) + break; // Nothing to do, we're already in the first column + if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) + targetColumn--; + else if (modifiers == Qt::ControlModifier || + modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) + scanForRegionBoundary(targetRow, targetColumn, 0, -1); + else + targetColumn--; //Unrecognized modifier combination: default to just moving one cell + tabCounter = 0; + break; + case Qt::Key_Right: + if (targetColumn >= this->model()->columnCount() - 1) + break; // Nothing to do, we're already in the last column + if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) + targetColumn += colSpan; + else if (modifiers == Qt::ControlModifier || + modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) + scanForRegionBoundary(targetRow, targetColumn, 0, 1); + else + targetColumn += colSpan; //Unrecognized modifier combination: default to just moving one cell + tabCounter = 0; + break; + case Qt::Key_Up: + if (targetRow == 0) + break; // Nothing to do, we're already in the first column + if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) + targetRow--; + else if (modifiers == Qt::ControlModifier || + modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) + scanForRegionBoundary(targetRow, targetColumn, -1, 0); + else + targetRow--; //Unrecognized modifier combination: default to just moving one cell + tabCounter = 0; + break; + case Qt::Key_Down: + if (targetRow >= this->model()->rowCount() - 1) + break; // Nothing to do, we're already in the last row + if (modifiers == Qt::NoModifier || modifiers == Qt::ShiftModifier) + targetRow += rowSpan; + else if (modifiers == Qt::ControlModifier || + modifiers == (Qt::ControlModifier | Qt::ShiftModifier)) + scanForRegionBoundary(targetRow, targetColumn, 1, 0); + else + targetRow += rowSpan; //Unrecognized modifier combination: default to just moving one cell + tabCounter = 0; + break; + case Qt::Key_Tab: + if (modifiers == Qt::NoModifier) { + tabCounter++; + if (handleTabMotion) + targetColumn += colSpan; + } + else if (modifiers == Qt::ShiftModifier) { + tabCounter = 0; + if (handleTabMotion) + targetColumn--; + } + break; + case Qt::Key_Backtab: + if (modifiers == Qt::NoModifier) { + targetColumn--; + } + tabCounter = 0; + break; + default: + break; + } + + if (this->sheet->isMergedCell(CellAddress(targetRow, targetColumn))) { + auto anchor = this->sheet->getAnchor(CellAddress(targetRow, targetColumn)); + targetRow = anchor.row(); + targetColumn = anchor.col(); + } + + // Overflow/underflow protection: + const int maxRow = this->model()->rowCount() - 1; + const int maxCol = this->model()->columnCount() - 1; + targetRow = std::max(0, std::min(targetRow, maxRow)); + targetColumn = std::max(0, std::min(targetColumn, maxCol)); + + if (!(modifiers & Qt::ShiftModifier) || keyPressed == Qt::Key_Tab || keyPressed == Qt::Key_Enter || keyPressed == Qt::Key_Return) { + // We have to use this method so that Ctrl-modifier combinations don't result in multiple selection + this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetColumn), + QItemSelectionModel::ClearAndSelect); + } + else if (modifiers & Qt::ShiftModifier) { + // With shift down, this motion becomes a block selection command, rather than just simple motion: + ModifyBlockSelection(targetRow, targetColumn); + } + +} + +void SheetTableView::ModifyBlockSelection(int targetRow, int targetCol) +{ + int startingRow = currentIndex().row(); + int startingCol = currentIndex().column(); + + // Get the current block selection size: + auto selection = this->selectionModel()->selection(); + for (const auto& range : selection) { + if (range.contains(currentIndex())) { + // This range contains the current cell, so it's the one we're going to modify (assuming we're at one of the corners) + int rangeMinRow = range.top(); + int rangeMaxRow = range.bottom(); + int rangeMinCol = range.left(); + int rangeMaxCol = range.right(); + if ((startingRow == rangeMinRow || startingRow == rangeMaxRow) && + (startingCol == rangeMinCol || startingCol == rangeMaxCol)) { + if (range.contains(model()->index(targetRow, targetCol))) { + // If the range already contains the target cell, then we're making the range smaller + if (startingRow == rangeMinRow) + rangeMinRow = targetRow; + if (startingRow == rangeMaxRow) + rangeMaxRow = targetRow; + if (startingCol == rangeMinCol) + rangeMinCol = targetCol; + if (startingCol == rangeMaxCol) + rangeMaxCol = targetCol; + } + else { + // We're making the range bigger + rangeMinRow = std::min(rangeMinRow, targetRow); + rangeMaxRow = std::max(rangeMaxRow, targetRow); + rangeMinCol = std::min(rangeMinCol, targetCol); + rangeMaxCol = std::max(rangeMaxCol, targetCol); + } + QItemSelection oldRange(range.topLeft(), range.bottomRight()); + this->selectionModel()->select(oldRange, QItemSelectionModel::Deselect); + QItemSelection newRange(model()->index(rangeMinRow, rangeMinCol), model()->index(rangeMaxRow, rangeMaxCol)); + this->selectionModel()->select(newRange, QItemSelectionModel::Select); + } + break; + } + } + + this->selectionModel()->setCurrentIndex(model()->index(targetRow, targetCol), QItemSelectionModel::Current); +} + +void SheetTableView::closeEditor(QWidget * editor, QAbstractItemDelegate::EndEditHint hint) +{ QTableView::closeEditor(editor, hint); - setCurrentIndex(le->next()); +} + +void SheetTableView::mousePressEvent(QMouseEvent* event) +{ + tabCounter = 0; + QTableView::mousePressEvent(event); } void SheetTableView::edit ( const QModelIndex & index ) diff --git a/src/Mod/Spreadsheet/Gui/SheetTableView.h b/src/Mod/Spreadsheet/Gui/SheetTableView.h index 5dbb7b3b05..828856fd4f 100644 --- a/src/Mod/Spreadsheet/Gui/SheetTableView.h +++ b/src/Mod/Spreadsheet/Gui/SheetTableView.h @@ -62,6 +62,9 @@ public: void copySelection(); void cutSelection(); void pasteClipboard(); + void finishEditWithMove(int keyPressed, Qt::KeyboardModifiers modifiers, bool handleTabMotion = false); + + void ModifyBlockSelection(int targetRow, int targetColumn); protected Q_SLOTS: void commitData(QWidget *editor); @@ -77,9 +80,11 @@ protected: bool edit(const QModelIndex &index, EditTrigger trigger, QEvent *event); bool event(QEvent *event); void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint); + void mousePressEvent(QMouseEvent* event); QModelIndex currentEditIndex; Spreadsheet::Sheet * sheet; + int tabCounter; boost::signals2::scoped_connection cellSpanChangedConnection; }; diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp index 835a0ec6af..6ced58e311 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.cpp @@ -46,29 +46,13 @@ QWidget *SpreadsheetDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const { + Q_UNUSED(index) SpreadsheetGui::LineEdit *editor = new SpreadsheetGui::LineEdit(parent); - editor->setIndex(index); - editor->setDocumentObject(sheet); - connect(editor, SIGNAL(returnPressed()), this, SLOT(commitAndCloseEditor())); + connect(editor, &SpreadsheetGui::LineEdit::finishedWithKey, this, &SpreadsheetDelegate::on_editorFinishedWithKey); return editor; } -void SpreadsheetDelegate::commitAndCloseEditor() -{ - Gui::ExpressionLineEdit *editor = qobject_cast(sender()); - if (editor->completerActive()) { - editor->hideCompleter(); - return; - } - - // See https://forum.freecadweb.org/viewtopic.php?f=3&t=41694 - // It looks like the slot commitAndCloseEditor() is not needed any more and even - // causes a crash when doing so because the LineEdit is still accessed after its destruction. - //Q_EMIT commitData(editor); - //Q_EMIT closeEditor(editor); -} - void SpreadsheetDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { @@ -89,6 +73,11 @@ void SpreadsheetDelegate::setModelData(QWidget *editor, } } +void SpreadsheetDelegate::on_editorFinishedWithKey(int key, Qt::KeyboardModifiers modifiers) +{ + Q_EMIT finishedWithKey(key, modifiers); +} + QSize SpreadsheetDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { Q_UNUSED(option); diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h index 03080af514..fe4efc7855 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetDelegate.h @@ -45,8 +45,10 @@ public: const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; +Q_SIGNALS: + void finishedWithKey(int key, Qt::KeyboardModifiers modifiers); private Q_SLOTS: - void commitAndCloseEditor(); + void on_editorFinishedWithKey(int key, Qt::KeyboardModifiers modifiers); private: Spreadsheet::Sheet * sheet; }; diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index 1bd97d7af6..3d2056cb3f 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -98,9 +98,12 @@ SheetView::SheetView(Gui::Document *pcDocument, App::DocumentObject *docObj, QWi connect(ui->cells->verticalHeader(), SIGNAL(sectionResized ( int, int, int ) ), this, SLOT(rowResized(int, int, int))); - connect(ui->cellContent, SIGNAL(returnPressed()), this, SLOT( editingFinished() )); - connect(ui->cellAlias, SIGNAL(returnPressed()), this, SLOT( editingFinished() )); - connect(ui->cellAlias, SIGNAL(textEdited(QString)), this, SLOT(aliasChanged(QString))); + connect(delegate, &SpreadsheetDelegate::finishedWithKey, this, &SheetView::editingFinishedWithKey); + connect(ui->cellContent, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmContentChanged(ui->cellContent->text()); }); + connect(ui->cellContent, &LineEdit::returnPressed, this, [this]() {confirmContentChanged(ui->cellContent->text()); }); + connect(ui->cellAlias, &LineEdit::finishedWithKey, this, [this](int, Qt::KeyboardModifiers) {confirmAliasChanged(ui->cellAlias->text()); }); + connect(ui->cellAlias, &LineEdit::returnPressed, this, [this]() {confirmAliasChanged(ui->cellAlias->text()); }); + connect(ui->cellAlias, &LineEdit::textEdited, this, &SheetView::aliasChanged); columnWidthChangedConnection = sheet->columnWidthChanged.connect(bind(&SheetView::resizeColumn, this, bp::_1, bp::_2)); rowHeightChangedConnection = sheet->rowHeightChanged.connect(bind(&SheetView::resizeRow, this, bp::_1, bp::_2)); @@ -215,20 +218,6 @@ void SheetView::setCurrentCell(QString str) updateAliasLine(); } -void SheetView::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_Delete) { - if (event->modifiers() == 0) { - //model()->setData(currentIndex(), QVariant(), Qt::EditRole); - } - else if (event->modifiers() == Qt::ControlModifier) { - //model()->setData(currentIndex(), QVariant(), Qt::EditRole); - } - } - else - Gui::MDIView::keyPressEvent(event); -} - void SheetView::updateContentLine() { QModelIndex i = ui->cells->currentIndex(); @@ -238,7 +227,6 @@ void SheetView::updateContentLine() if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column()))) (void)cell->getStringContent(str); ui->cellContent->setText(QString::fromUtf8(str.c_str())); - ui->cellContent->setIndex(i); ui->cellContent->setEnabled(true); // Update completer model; for the time being, we do this by setting the document object of the input line. @@ -255,7 +243,6 @@ void SheetView::updateAliasLine() if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column()))) (void)cell->getAlias(str); ui->cellAlias->setText(QString::fromUtf8(str.c_str())); - ui->cellAlias->setIndex(i); ui->cellAlias->setEnabled(true); // Update completer model; for the time being, we do this by setting the document object of the input line. @@ -322,51 +309,53 @@ void SheetView::resizeRow(int col, int newSize) ui->cells->setRowHeight(col, newSize); } -void SheetView::editingFinished() +void SheetView::editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers) { - if (ui->cellContent->completerActive()) { - ui->cellContent->hideCompleter(); - return; - } - - if (ui->cellAlias->completerActive()) { - ui->cellAlias->hideCompleter(); - return; - } - QModelIndex i = ui->cells->currentIndex(); if (i.isValid()) { - QString str = ui->cellAlias->text(); - bool aliasOkay = true; - - if (str.length()!= 0 && !sheet->isValidAlias(Base::Tools::toStdString(str))){ - aliasOkay = false; - } - - ui->cellAlias->setDocumentObject(sheet); ui->cells->model()->setData(i, QVariant(ui->cellContent->text()), Qt::EditRole); + ui->cells->finishEditWithMove(key, modifiers); + } +} - if (const auto * cell = sheet->getCell(CellAddress(i.row(), i.column()))){ - if (!aliasOkay){ - //do not show error message if failure to set new alias is because it is already the same string - std::string current_alias; - (void)cell->getAlias(current_alias); - if (str != QString::fromUtf8(current_alias.c_str())){ - Base::Console().Error("Unable to set alias: %s\n", Base::Tools::toStdString(str).c_str()); - } - } else { - std::string address = CellAddress(i.row(), i.column()).toString(); - Gui::cmdAppObjectArgs(sheet, "setAlias('%s', '%s')", - address, str.toStdString()); - Gui::cmdAppDocument(sheet->getDocument(), "recompute()"); +void SheetView::confirmAliasChanged(const QString& text) +{ + bool aliasOkay = true; + + ui->cellAlias->setDocumentObject(sheet); + if (text.length() != 0 && !sheet->isValidAlias(Base::Tools::toStdString(text))) { + aliasOkay = false; + } + + QModelIndex i = ui->cells->currentIndex(); + if (const auto* cell = sheet->getCell(CellAddress(i.row(), i.column()))) { + if (!aliasOkay) { + //do not show error message if failure to set new alias is because it is already the same string + std::string current_alias; + (void)cell->getAlias(current_alias); + if (text != QString::fromUtf8(current_alias.c_str())) { + Base::Console().Error("Unable to set alias: %s\n", Base::Tools::toStdString(text).c_str()); } } - ui->cells->setCurrentIndex(ui->cellContent->next()); - ui->cells->setFocus(); + else { + std::string address = CellAddress(i.row(), i.column()).toString(); + Gui::cmdAppObjectArgs(sheet, "setAlias('%s', '%s')", + address, text.toStdString()); + Gui::cmdAppDocument(sheet->getDocument(), "recompute()"); + ui->cells->setFocus(); + } } } + +void SheetView::confirmContentChanged(const QString& text) +{ + QModelIndex i = ui->cells->currentIndex(); + ui->cells->model()->setData(i, QVariant(text), Qt::EditRole); + ui->cells->setFocus(); +} + void SheetView::aliasChanged(const QString& text) { // check live the input and highlight if the user input invalid characters diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h index 8e6aced3ef..141cdfe59b 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.h +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.h @@ -82,8 +82,10 @@ public: virtual void deleteSelf(); protected Q_SLOTS: - void editingFinished(); + void editingFinishedWithKey(int key, Qt::KeyboardModifiers modifiers); + void confirmAliasChanged(const QString& text); void aliasChanged(const QString& text); + void confirmContentChanged(const QString& text); void currentChanged( const QModelIndex & current, const QModelIndex & previous ); void columnResized(int col, int oldSize, int newSize); void rowResized(int row, int oldSize, int newSize); @@ -94,7 +96,6 @@ protected: void updateContentLine(); void updateAliasLine(); void setCurrentCell(QString str); - void keyPressEvent(QKeyEvent *event); void resizeColumn(int col, int newSize); void resizeRow(int col, int newSize); diff --git a/src/Tools/generateBase/generateTools.py b/src/Tools/generateBase/generateTools.py index d6d6ad92b1..2e4efa3243 100644 --- a/src/Tools/generateBase/generateTools.py +++ b/src/Tools/generateBase/generateTools.py @@ -100,12 +100,15 @@ class copier: except TypeError: self.ouf.write(self.regex.sub(repl, line)) i=i+1 - def __init__(self, regex=_never, dict={}, + def __init__(self, regex=_never, dict=None, restat=_never, restend=_never, recont=_never, preproc=identity, handle=nohandle, ouf=sys.stdout): "Initialize self's attributes" self.regex = regex - self.globals = dict + if dict is not None: + self.globals = dict + else: + self.globals = {} self.globals['sys'] = sys self.locals = { '_cb':self.copyblock } self.restat = restat