Skip to content

Commit 93fdf85

Browse files
authored
Merge branch 'main' into CURA-12556_formula-parser-in-CuraEngine
2 parents 83d1a73 + 2e1a12c commit 93fdf85

File tree

8,247 files changed

+9970
-8453
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

8,247 files changed

+9970
-8453
lines changed

cura/CuraApplication.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ class CuraApplication(QtApplication):
140140
# SettingVersion represents the set of settings available in the machine/extruder definitions.
141141
# You need to make sure that this version number needs to be increased if there is any non-backwards-compatible
142142
# changes of the settings.
143-
SettingVersion = 25
143+
SettingVersion = 26
144144

145145
Created = False
146146

plugins/PaintTool/PaintTool.py

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def __init__(self, view: PaintView) -> None:
5151
self._view: PaintView = view
5252
self._view.canUndoChanged.connect(self._onCanUndoChanged)
5353
self._view.canRedoChanged.connect(self._onCanRedoChanged)
54+
self._view.currentPaintedObjectMeshDataChanged.connect(self._updateState)
5455

5556
self._picking_pass: Optional[PickingPass] = None
5657
self._faces_selection_pass: Optional[SelectionPass] = None
@@ -198,40 +199,6 @@ def clear(self) -> None:
198199
self._view.clearPaint()
199200
self._updateScene(update_node = True)
200201

201-
@staticmethod
202-
def _get_intersect_ratio_via_pt(a: numpy.ndarray, pt: numpy.ndarray, b: numpy.ndarray, c: numpy.ndarray) -> float:
203-
# compute the intersection of (param) A - pt with (param) B - (param) C
204-
if all(a == pt) or all(b == c) or all(a == c) or all(a == b):
205-
return 1.0
206-
207-
# compute unit vectors of directions of lines A and B
208-
udir_a = a - pt
209-
udir_a /= numpy.linalg.norm(udir_a)
210-
udir_b = b - c
211-
udir_b /= numpy.linalg.norm(udir_b)
212-
213-
# find unit direction vector for line C, which is perpendicular to lines A and B
214-
udir_res = numpy.cross(udir_b, udir_a)
215-
udir_res_len = numpy.linalg.norm(udir_res)
216-
if udir_res_len == 0:
217-
return 1.0
218-
udir_res /= udir_res_len
219-
220-
# solve system of equations
221-
rhs = b - a
222-
lhs = numpy.array([udir_a, -udir_b, udir_res]).T
223-
try:
224-
solved = numpy.linalg.solve(lhs, rhs)
225-
except numpy.linalg.LinAlgError:
226-
return 1.0
227-
228-
# get the ratio
229-
intersect = ((a + solved[0] * udir_a) + (b + solved[1] * udir_b)) * 0.5
230-
a_intersect_dist = numpy.linalg.norm(a - intersect)
231-
if a_intersect_dist == 0:
232-
return 1.0
233-
return numpy.linalg.norm(pt - intersect) / a_intersect_dist
234-
235202
def _nodeTransformChanged(self, *args) -> None:
236203
self._cache_dirty = True
237204

@@ -453,7 +420,7 @@ def _updateActiveView(self) -> None:
453420
def _updateState(self):
454421
painted_object = self._view.getPaintedObject()
455422
if painted_object is not None and self._controller.getActiveTool() == self:
456-
if painted_object.callDecoration("getPaintTexture") is not None:
423+
if painted_object.callDecoration("getPaintTexture") is not None and painted_object.getMeshData().hasUVCoordinates():
457424
new_state = PaintTool.Paint.State.READY
458425
else:
459426
new_state = PaintTool.Paint.State.PREPARING_MODEL
@@ -472,6 +439,7 @@ def _onPrepareTextureFinished(self, job: Job):
472439
self._prepare_texture_job = None
473440
self._state = PaintTool.Paint.State.READY
474441
self.propertyChanged.emit()
442+
self._updateScene()
475443

476444
def _updateIgnoreUnselectedObjects(self):
477445
ignore_unselected_objects = self._controller.getActiveView().name == "PaintTool"

plugins/PaintTool/PaintView.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ def __init__(self) -> None:
6161

6262
canUndoChanged = pyqtSignal(bool)
6363
canRedoChanged = pyqtSignal(bool)
64+
currentPaintedObjectMeshDataChanged = pyqtSignal()
6465

6566
def setPaintedObject(self, painted_object: Optional[SceneNode]):
6667
if self._painted_object is not None:
6768
texture_changed_signal = self._painted_object.callDecoration("getPaintTextureChangedSignal")
6869
texture_changed_signal.disconnect(self._onCurrentPaintedObjectTextureChanged)
70+
self._painted_object.meshDataChanged.disconnect(self._onCurrentPaintedObjectMesDataChanged)
6971

7072
self._paint_texture = None
7173
self._cursor_texture = None
@@ -78,6 +80,7 @@ def setPaintedObject(self, painted_object: Optional[SceneNode]):
7880
if texture_changed_signal is not None:
7981
texture_changed_signal.connect(self._onCurrentPaintedObjectTextureChanged)
8082
self._onCurrentPaintedObjectTextureChanged()
83+
self._painted_object.meshDataChanged.connect(self._onCurrentPaintedObjectMesDataChanged)
8184

8285
self._updateCurrentBitsRanges()
8386

@@ -99,6 +102,10 @@ def _onCurrentPaintedObjectTextureChanged(self) -> None:
99102
else:
100103
self._cursor_texture = None
101104

105+
def _onCurrentPaintedObjectMesDataChanged(self, object: SceneNode) -> None:
106+
if object == self._painted_object:
107+
self.currentPaintedObjectMeshDataChanged.emit()
108+
102109
def canUndo(self):
103110
stack = self._getUndoStack()
104111
return stack.canUndo() if stack is not None else False

plugins/PostProcessingPlugin/PostProcessingPlugin.qml

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,42 @@ UM.Dialog
3939

4040
anchors.fill: parent
4141

42+
// Helper function to check if a setting should use multiline text area
43+
// Supports "multiline" or "@[multiline]" or "@[multiline, other] comment"
44+
function isMultilineSetting(definition)
45+
{
46+
if (!definition || !definition.comments)
47+
{
48+
return false;
49+
}
50+
51+
var commentsLower = definition.comments.toLowerCase();
52+
53+
// Simple format: exact match
54+
if (commentsLower === "multiline")
55+
{
56+
return true;
57+
}
58+
59+
// Directive format: parse @[...] and check if multiline is in the list
60+
var directiveStart = commentsLower.indexOf("@[");
61+
var directiveEnd = commentsLower.indexOf("]", directiveStart);
62+
if (directiveStart >= 0 && directiveEnd > directiveStart)
63+
{
64+
var directivesText = commentsLower.substring(directiveStart + 2, directiveEnd);
65+
var directives = directivesText.split(",");
66+
for (var i = 0; i < directives.length; i++)
67+
{
68+
if (directives[i].trim() === "multiline")
69+
{
70+
return true;
71+
}
72+
}
73+
}
74+
75+
return false;
76+
}
77+
4278
ButtonGroup
4379
{
4480
id: selectedScriptGroup
@@ -301,6 +337,10 @@ UM.Dialog
301337
{
302338
if (provider.properties.enabled == "True" && model.type != undefined)
303339
{
340+
if (definition && definition.comments && definition.comments.toLowerCase() === "multiline")
341+
{
342+
return UM.Theme.getSize("standard_list_lineheight").height + UM.Theme.getSize("narrow_margin").height + (UM.Theme.getSize("setting_control").height * 3);
343+
}
304344
return UM.Theme.getSize("section").height;
305345
}
306346
else
@@ -331,6 +371,19 @@ UM.Dialog
331371
settingLoader.item.showLinkedSettingIcon = false
332372
settingLoader.item.doDepthIndentation = false
333373
settingLoader.item.doQualityUserSettingEmphasis = false
374+
// Pass properties explicitly to custom components that don't extend SettingItem
375+
if (settingLoader.item.hasOwnProperty("definition")) {
376+
settingLoader.item.definition = settingLoader.definition
377+
}
378+
if (settingLoader.item.hasOwnProperty("settingDefinitionsModel")) {
379+
settingLoader.item.settingDefinitionsModel = settingLoader.settingDefinitionsModel
380+
}
381+
if (settingLoader.item.hasOwnProperty("propertyProvider")) {
382+
settingLoader.item.propertyProvider = settingLoader.propertyProvider
383+
}
384+
if (settingLoader.item.hasOwnProperty("globalPropertyProvider")) {
385+
settingLoader.item.globalPropertyProvider = settingLoader.globalPropertyProvider
386+
}
334387
}
335388

336389
sourceComponent:
@@ -348,7 +401,7 @@ UM.Dialog
348401
case "bool":
349402
return settingCheckBox
350403
case "str":
351-
return settingTextField
404+
return base.isMultilineSetting(definition) ? settingTextArea : settingTextField
352405
case "category":
353406
return settingCategory
354407
default:
@@ -405,6 +458,13 @@ UM.Dialog
405458
Cura.SettingTextField { }
406459
}
407460

461+
Component
462+
{
463+
id: settingTextArea;
464+
465+
SettingTextArea { }
466+
}
467+
408468
Component
409469
{
410470
id: settingComboBox;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) 2025 Ultimaker B.V.
2+
// Cura is released under the terms of the LGPLv3 or higher.
3+
4+
import QtQuick 2.15
5+
import QtQuick.Controls 2.15
6+
7+
import UM 1.7 as UM
8+
9+
// Custom multiline text area component for post-processing script settings
10+
// Triggered when setting has "comments": "multiline" property
11+
Item
12+
{
13+
id: base
14+
15+
// Properties passed from the Loader
16+
property var definition
17+
property var settingDefinitionsModel
18+
property var propertyProvider
19+
property var globalPropertyProvider
20+
21+
// Standard setting item properties (required by loader but unused in this component)
22+
property bool showRevertButton: false
23+
property bool showInheritButton: false
24+
property bool showLinkedSettingIcon: false
25+
property bool doDepthIndentation: false
26+
property bool doQualityUserSettingEmphasis: false
27+
28+
// Internal state tracking (unused but kept for potential future use)
29+
property string textBeforeEdit
30+
property bool textHasChanged
31+
32+
// Signals for tooltip support (required by Connections in loader)
33+
// Note: These signals are declared but intentionally never emitted.
34+
// This prevents tooltips from appearing on mouse-over, which would obstruct the text area
35+
// while the user is typing or editing multiline content.
36+
signal showTooltip(string text)
37+
signal hideTooltip()
38+
39+
width: parent.width
40+
// Height calculation: label height + spacing + text area (3x normal height for multiline editing)
41+
height: UM.Theme.getSize("standard_list_lineheight").height + UM.Theme.getSize("narrow_margin").height + (UM.Theme.getSize("setting_control").height * 3)
42+
43+
UM.Label
44+
{
45+
id: labelText
46+
anchors.top: parent.top
47+
anchors.left: parent.left
48+
anchors.right: parent.right
49+
height: UM.Theme.getSize("standard_list_lineheight").height
50+
text: definition ? definition.label : ""
51+
elide: Text.ElideRight
52+
font: UM.Theme.getFont("default_bold")
53+
color: UM.Theme.getColor("text")
54+
}
55+
56+
Flickable
57+
{
58+
id: flickable
59+
anchors.top: labelText.bottom
60+
anchors.topMargin: UM.Theme.getSize("narrow_margin").height
61+
anchors.left: parent.left
62+
anchors.right: parent.right
63+
// Right margin to prevent overlap with parent ListView scrollbar
64+
anchors.rightMargin: UM.Theme.getSize("default_margin").width + UM.Theme.getSize("narrow_margin").width
65+
height: UM.Theme.getSize("setting_control").height * 3
66+
67+
clip: true
68+
ScrollBar.vertical: UM.ScrollBar { }
69+
70+
TextArea.flickable: TextArea
71+
{
72+
id: textArea
73+
74+
enabled: propertyProvider && propertyProvider.properties ? propertyProvider.properties.enabled === "True" : true
75+
// Explicit undefined check to prevent QML warnings about undefined QString assignment
76+
text: (propertyProvider && propertyProvider.properties && propertyProvider.properties.value !== undefined) ? propertyProvider.properties.value : ""
77+
78+
background: Rectangle
79+
{
80+
color: UM.Theme.getColor("setting_control")
81+
border.color: textArea.activeFocus ? UM.Theme.getColor("text_field_border_active") : UM.Theme.getColor("text_field_border")
82+
border.width: UM.Theme.getSize("default_lining").width
83+
}
84+
85+
onTextChanged:
86+
{
87+
// Save value on each keystroke when focused (live update)
88+
if (activeFocus && propertyProvider)
89+
{
90+
propertyProvider.setPropertyValue("value", text);
91+
}
92+
}
93+
94+
font: UM.Theme.getFont("default")
95+
color: UM.Theme.getColor("text")
96+
selectionColor: UM.Theme.getColor("text_selection")
97+
selectedTextColor: UM.Theme.getColor("text")
98+
wrapMode: TextEdit.NoWrap
99+
selectByMouse: true
100+
101+
// Allow Enter/Return to insert newlines instead of closing dialog
102+
Keys.onReturnPressed: function(event) { event.accepted = false; }
103+
Keys.onEnterPressed: function(event) { event.accepted = false; }
104+
// Escape key removes focus from text area
105+
Keys.onEscapePressed: function(event) { focus = false; event.accepted = true; }
106+
}
107+
}
108+
}

plugins/PostProcessingPlugin/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "Post Processing",
33
"author": "Ultimaker",
4-
"version": "2.2.1",
4+
"version": "2.3.0",
55
"api": 8,
66
"description": "Extension that allows for user created scripts for post processing",
77
"catalog": "cura"

0 commit comments

Comments
 (0)