Sketcher: Text: Fixes

This commit is contained in:
paddle
2026-03-10 07:22:25 +01:00
committed by Benjamin Nauck
parent 320bd61156
commit d842e4151c
14 changed files with 145 additions and 34 deletions
+2 -2
View File
@@ -7507,7 +7507,7 @@ void transformAndConvertToGeometry(
double baseHeight = ymax - ymin;
// This transform will move the geometry's bottom-left corner to the origin (0,0,0)
gp_Vec initialTranslationVec(-xmin, -ymin, -zmin);
gp_Vec initialTranslationVec(-xmin, -ymin, 0.0);
// 2. Determine scale and rotation
double angle;
@@ -7536,7 +7536,7 @@ void transformAndConvertToGeometry(
gp_Trsf rotateTrsf;
rotateTrsf.SetRotation(gp::XOY().Axis(), angle);
gp_Trsf finalTranslate;
finalTranslate.SetTranslation(gp_Vec(p1.x, p1.y, p1.z));
finalTranslate.SetTranslation(gp_Vec(p1.x, p1.y, 0.0));
gp_Trsf finalTrsf = finalTranslate * rotateTrsf * scaleTrsf * initialTranslate;
// 4. Apply transformation and convert to Sketcher geometry
+1 -1
View File
@@ -5662,7 +5662,7 @@ void Sketch::applyGroupTransformations()
Base::Matrix4D T2; // Identity
T2[0][3] = postSolveFrame.startPoint.x;
T2[1][3] = postSolveFrame.startPoint.y;
T2[2][3] = postSolveFrame.startPoint.z;
T2[2][3] = 0;
// 5. Combine the matrices in the correct order: T_final = T2 * R * S * T1
Base::Matrix4D transform = T2 * R * S * T1;
+3 -2
View File
@@ -906,7 +906,7 @@ double SketchObject::getDatum(int ConstrId) const
return this->Constraints[ConstrId]->getValue();
}
int SketchObject::setTextAndFont(int ConstrId, std::string& newText, std::string& newFont, bool isConstruction)
int SketchObject::setTextAndFont(int ConstrId, std::string& newText, std::string& newFont, bool isHeight, bool isConstruction)
{
; // no need to check input data validity as this is an sketchobject managed operation.
Base::StateLocker lock(managedoperation, true);
@@ -926,9 +926,9 @@ int SketchObject::setTextAndFont(int ConstrId, std::string& newText, std::string
}
// First we replace the old geometries by the new text.
const bool isHeight = constr->getIsTextHeight();
const std::string oldText = constr->getText();
const std::string oldFont = constr->getFont();
const bool oldIsHeight = constr->getIsTextHeight();
int handleGeoId = constr->getGeoId(0);
int firstTextGeoId = constr->getGeoId(1);
bool hasExistingText = firstTextGeoId != GeoEnum::GeoUndef;
@@ -1012,6 +1012,7 @@ int SketchObject::setTextAndFont(int ConstrId, std::string& newText, std::string
if (err) {
constr->setText(oldText);
constr->setFont(oldFont);
constr->setIsTextHeight(oldIsHeight);
}
return err;
+1
View File
@@ -358,6 +358,7 @@ public:
int ConstrId,
std::string& newText,
std::string& newFont,
bool isHeight,
bool isConstruction = false
);
/// set the driving status of this constraint and solve
+6 -2
View File
@@ -409,16 +409,20 @@ class SketchObject(Part2DObject):
"""
...
def setTextAndFont(self, constraint: int, text: str, font: str) -> None:
def setTextAndFont(
self, constraint: int, text: str, font: str, isheight: bool, isConstruction: bool
) -> None:
"""
Set the text and font of a Text constraint.
setTextAndFont(constraint: int, text: str, font: str)
setTextAndFont(constraint: int, text: str, font: str, isHeight: bool, isConstruction: bool)
Args:
constraint: The index of the Text constraint.
text: The text string to display.
font: The full path to the font file (.ttf, .otf, etc.).
isHeight: Is the line handle of the group the height of the text.
isConstruction: Are text geometry construction of not.
"""
...
+20 -5
View File
@@ -761,11 +761,21 @@ PyObject* SketchObjectPy::setTextAndFont(PyObject* args, PyObject* kwd)
int constrIndex = -1;
char* textStr;
char* fontStr;
char* constrName = nullptr;
PyObject* isHeightObj = Py_True;
PyObject* isConstrObj = Py_False; // Default to null (parameter not provided)
// "iss|O" means: int, string, string, | optional Object
if (!PyArg_ParseTuple(args, "iss|O!", &constrIndex, &textStr, &fontStr, &PyBool_Type, &isConstrObj)) {
// "iss|O!O!" (int, str, str, | bool, bool)
if (!PyArg_ParseTuple(
args,
"iss|O!O!",
&constrIndex,
&textStr,
&fontStr,
&PyBool_Type,
&isHeightObj,
&PyBool_Type,
&isConstrObj
)) {
return nullptr;
}
@@ -773,8 +783,13 @@ PyObject* SketchObjectPy::setTextAndFont(PyObject* args, PyObject* kwd)
std::string font(fontStr);
// Call the C++ implementation
int err = this->getSketchObjectPtr()
->setTextAndFont(constrIndex, text, font, Base::asBoolean(isConstrObj));
int err = this->getSketchObjectPtr()->setTextAndFont(
constrIndex,
text,
font,
Base::asBoolean(isHeightObj),
Base::asBoolean(isConstrObj)
);
// Handle errors returned from the C++ function
if (err) {
+14 -14
View File
@@ -54,20 +54,20 @@ enum class FilterValue
Equality = 9,
Symmetric = 10,
Block = 11,
InternalAlignment = 12,
Datums = 13,
HorizontalDistance = 14,
VerticalDistance = 15,
Distance = 16,
Radius = 17,
Weight = 18,
Diameter = 19,
Angle = 20,
SnellsLaw = 21,
Named = 22,
NonDriving = 23,
Group = 24,
Text = 25,
Group = 12,
Text = 13,
InternalAlignment = 14,
Datums = 15,
HorizontalDistance = 16,
VerticalDistance = 17,
Distance = 18,
Radius = 19,
Weight = 20,
Diameter = 21,
Angle = 22,
SnellsLaw = 23,
Named = 24,
NonDriving = 25,
NumFilterValue // SpecialFilterValue shall start at the same index as this
};
+8 -3
View File
@@ -145,6 +145,7 @@ private:
std::string escFont = escapeForPython(font);
bool isHeight = constructionMethod() == ConstructionMethod::Height;
const char* constrBoolStr = isConstructionMode() ? "True" : "False";
const char* heightBoolStr = isHeight ? "True" : "False";
// Add the 'Text' Constraint (Empty)
// We initialize the constraint containing ONLY the handle (element 0).
@@ -156,7 +157,7 @@ private:
handleId,
escText.c_str(),
escFont.c_str(),
isHeight ? "True" : "False"
heightBoolStr
);
// Generate Text Geometry by calling setTextAndFont on the new constraint.
@@ -165,10 +166,11 @@ private:
Gui::cmdAppObjectArgs(
getSketchObject(),
"setTextAndFont(len(App.ActiveDocument.getObject('%s').Constraints)-1, '%s', '%s', "
"%s)",
"%s, %s)",
getSketchObject()->getNameInDocument(),
escText.c_str(),
escFont.c_str(),
heightBoolStr,
constrBoolStr
);
@@ -383,7 +385,10 @@ void DSHTextController::configureToolWidget()
// 3. Set a sensible default font
QString defaultFontName;
if (fontNames.contains(QString::fromUtf8("DejaVu Sans"), Qt::CaseInsensitive)) {
if (fontNames.contains(QString::fromUtf8("osifont-lgpl3fe"), Qt::CaseInsensitive)) {
defaultFontName = QString::fromUtf8("osifont-lgpl3fe");
}
else if (fontNames.contains(QString::fromUtf8("DejaVu Sans"), Qt::CaseInsensitive)) {
defaultFontName = QString::fromUtf8("DejaVu Sans");
}
else if (fontNames.contains(QString::fromUtf8("Arial"), Qt::CaseInsensitive)) {
+25 -2
View File
@@ -53,6 +53,9 @@ EditTextDialog::EditTextDialog(ViewProviderSketch* viewProvider, int constraintI
// Initialize Text
ui->lineEdit_text->setText(QString::fromStdString(constraint->getText()));
ui->radioButton_height->setChecked(constraint->getIsTextHeight());
ui->radioButton_width->setChecked(!constraint->getIsTextHeight());
// Initialize Font
populateFontList();
QString currentFontName = findFontNameFromPath(QString::fromStdString(constraint->getFont()));
@@ -87,11 +90,13 @@ void EditTextDialog::on_buttonBox_accepted()
std::string newText = ui->lineEdit_text->text().toStdString();
QString selectedFontName = ui->comboBox_font->currentText();
std::string newFontPath = fontPathMap.value(selectedFontName).toStdString();
bool newIsHeight = ui->radioButton_height->isChecked();
const Sketcher::Constraint* constraint = sketch->Constraints[constrIndex];
// Check if anything changed
if (newText == constraint->getText() && newFontPath == constraint->getFont()) {
if (newText == constraint->getText() && newFontPath == constraint->getFont()
&& newIsHeight == constraint->getIsTextHeight()) {
return; // Nothing to do
}
@@ -99,7 +104,25 @@ void EditTextDialog::on_buttonBox_accepted()
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Modify sketch text constraint"));
try {
Gui::cmdAppObjectArgs(sketch, "setTextAndFont(%i, '%s', '%s')", constrIndex, newText, newFontPath);
// Find if it was construction geometry to preserve that state
int firstTextGeoId = constraint->getGeoId(1);
bool isConstruction = false;
if (firstTextGeoId != Sketcher::GeoEnum::GeoUndef) {
isConstruction = Sketcher::GeometryFacade::getConstruction(
sketch->getGeometry(firstTextGeoId)
);
}
// Send the updated 5-parameter call to Python
Gui::cmdAppObjectArgs(
sketch,
"setTextAndFont(%i, '%s', '%s', %s, %s)",
constrIndex,
newText.c_str(),
newFontPath.c_str(),
newIsHeight ? "True" : "False",
isConstruction ? "True" : "False"
);
Gui::Command::commitCommand();
}
+18
View File
@@ -36,6 +36,24 @@
<item row="1" column="1">
<widget class="QComboBox" name="comboBox_font"/>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="layoutType">
<item>
<widget class="QRadioButton" name="radioButton_height">
<property name="text">
<string>Height</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButton_width">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
@@ -834,6 +834,10 @@ ConstraintFilterList::ConstraintFilterList(QWidget* parent)
it->setCheckState(isChecked ? Qt::Checked : Qt::Unchecked);
filterState = filterState >> 1;// shift right to get rid of the used bit.
}
// Text constraint filter is hidden from the user
item(static_cast<int>(ConstraintFilter::FilterValue::Text))->setHidden(true);
languageChange();
setPartiallyChecked();
@@ -119,6 +119,8 @@ private:
{QT_TR_NOOP("Equality"), 1},
{QT_TR_NOOP("Symmetric"), 1},
{QT_TR_NOOP("Block"), 1},
{QT_TR_NOOP("Group"), 1},
{QT_TR_NOOP("Text"), 1},
{QT_TR_NOOP("Internal Alignment"), 1},
{QT_TR_NOOP("Datums"), 0},
{QT_TR_NOOP("Horizontal Distance"), 1},
+5
View File
@@ -1003,6 +1003,11 @@ QMap<QString, QString> SketcherGui::findAvailableFontFiles()
QMap<QString, QString> fontMap;
QStringList fontPaths;
// 0. Include FreeCAD bundled fonts
fontPaths << QString::fromStdString(
App::Application::getResourceDir() + "Mod/TechDraw/Resources/fonts/"
);
#if defined(Q_OS_WIN)
fontPaths << QString::fromUtf8("C:/Windows/Fonts");
#elif defined(Q_OS_MACOS)
+36 -3
View File
@@ -1336,9 +1336,32 @@ void ViewProviderSketch::editDoubleClicked()
Base::Console().log("double click point:%d\n", preselection.PreselectPoint);
}
else if (preselection.isPreselectCurveValid()) {
// We cannot do toggleWireSelelection directly here because the released event with
//STATUS_NONE return false which clears the selection.
setSketchMode(STATUS_SELECT_Wire);
int geoId = preselection.PreselectCurve;
Sketcher::SketchObject* sketch = getSketchObject();
// Check if the preselected edge is the handle of a Text constraint
int textConstrId = -1;
const auto& constraints = sketch->Constraints.getValues();
for (int i = 0; i < static_cast<int>(constraints.size()); ++i) {
if (constraints[i]->Type == Sketcher::Text && constraints[i]->hasElement(0)) {
if (constraints[i]->getGeoId(0) == geoId) {
textConstrId = i;
break;
}
}
}
if (textConstrId != -1) {
Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Modify Text constraint"));
EditTextDialog editTextDialog(this, textConstrId);
editTextDialog.exec();
setSketchMode(STATUS_NONE);
}
else {
// We cannot do toggleWireSelelection directly here because the released event with
//STATUS_NONE return false which clears the selection.
setSketchMode(STATUS_SELECT_Wire);
}
}
else if (preselection.isCrossPreselected()) {
Base::Console().log("double click cross:%d\n",
@@ -2499,6 +2522,16 @@ bool ViewProviderSketch::detectAndShowPreselection(SoPickedPoint* Point)
}
else if (result.GeoIndex != -1
&& result.GeoIndex != preselection.PreselectCurve) {// if a new curve is hit
// If the picked edge is part of a text/group, treat the handle as the preselected item
int handleId = getSketchObject()->getGroupHandleIfInGroup(result.GeoIndex);
if (handleId != result.GeoIndex) {
if (handleId == preselection.PreselectCurve) {
return false;
}
result.GeoIndex = handleId;
}
std::stringstream ss;
if (result.GeoIndex >= 0)
ss << "Edge" << result.GeoIndex + 1;