diff --git a/Documentation/Classes/constraintsDelegate.md b/Documentation/Classes/constraintsDelegate.md
new file mode 100644
index 0000000..a52db78
--- /dev/null
+++ b/Documentation/Classes/constraintsDelegate.md
@@ -0,0 +1,108 @@
+# constraintsDelegate
+
+The `constraintsDelegate` class retains the rules for moving and resizing form objects, and manages them during resizing.
+
+> 📌 Note that, for the moment, only horizontal rules are supported.
+> (I've never needed vertical constraints, but perhaps I will in the future...).
+
+## Properties
+
+|Properties|Description|Type|default|
+|:----------|:-----------|:-----------:|:-----------|
+|**.rules** | The constraints [rules](#rule) |`Collection`| [ ]
+|**.scrollBarWidth** | Width of scroll bar |`Integer`| macOS = 15px
Windows = 15px
+|**.marginH** | Default horizontal spacing between items |`Integer`| macOS = 20px
Windows = 20px
+|**.labelMargin** | Default spacing between label and input box |`Integer`| macOS = 10px
Windows = 10px
+|**.offset** | Default offset |`Integer`| macOS = 2px
Windows = 2px
+
+
+| Functions | Action |
+|:-------- |:------ |
+|.**load**(file: `4D.File`) | Load rules and default values from a file|
+|.**setMetrics**(metrics: `Object`) | Allow to set in one time the default values|
+|.**add**(rule: `Object`)  → `cs.constraintsDelegate`| Stores a [rule](#rule) or a [set of rules](#set)|
+|.**apply**() | Dealing with constraints|
+
+
+|**.set** | A collection of [rule objects](#rule)|`Collection`| All others properties are ignored
+
+## Rule object
+
+A rule can have one or more of the following properties.
+
+|Properties|Description|Type||
+|:----------|:-----------|:-----------:|:-----------|
+|**.formula** | A formula that will be executed|`Text` \| `4D.Function`| All others properties are ignored
+|**.target** | The item to which the rule applies.
**Mandatory if not a [set of rules](#set)**|`Text` \| `widget`|
It can be a comma-separated list of names
+|**.type** |[Constraint](#constraint) type.
**Mandatory if formula is null**|`Text`|
+|**.alignment** | Alignment for certain constraints |`Text`|
+|**.value** | Value in percent or px|`Real`| \*
+|**.margin** | Margin to respect in px |`Integer`|\*\*
+|**.reference** | Name of the element to take as reference|`Text`| Default is current viewport
(dialog or container size)
+|**.label**|Name of the associated label|`Text`| \*\*\*
+|**.toDelete**|**True** if the rule should be deleted after application|`Boolean`|Enables a rule to be applied only once, usually on loading.
+
+\* for `right`, `left`, `minimum-width`, `maximum-width`, `tile` & `float` rules
+
+\*\* for `fit-width`, `margin` left or right, `horizontal-alignment` left or right, `anchor` left or right, `inline` & `float` left or right
+
+\*\*\* If ommitted use "\.label" if exists
+
+## Set of rules
+
+Allow to store more than one rule for a same target.
+
+|Properties|Description|Type||
+|:----------|:-----------|:-----------:|:-----------|
+|**.target** | The item to which the rule applies.
Mandatory!|`Text` \| `widget`|
It can be a comma-separated list of names
+|**.set** | Collection of [rule objects](#rule) without target|`Collection`|All others properties are ignored
+
+
+## Constraint types
+
+|Properties|Description| |
+|:----------|:-----------|:-----------|
+|"right"|Keeps the right edge of an element as % of the ref|`value` From 0 to 1 or 1 to 100
+|"left"|Keeps the left edge of an element as % of the ref|`value` From 0 to 1 or 1 to 100
+|"minimum-width"|Sets the minimum width of an element|`value` = limit in px
+|"maximum-width"|Sets the maximum width of an element|`value` = limit in px
+|"fit-width"|Adjusts width to ref |Optional `margin` left & right in px, default is 0
+|"margin"|Maintains a horizontal margin from the ref |`alignment` should be "auto", "left" or "right"
+|"horizontal-alignment"| Maintains vertical alignment with the reference |`alignment` should be "center", "left" or "right".
`margin` if passed is used for "left" & "right" alignments
+|"anchor"| Maintains vertical alignment with the reference |`alignment` should be "center", "left" or "right".
`margin` if passed is used for "left" & "right" alignments
+|"inline"| Maintains the element at the right of the reference | use `margin`if passed, `marginH` if not
+|"tile"| Set element width as percent of the reference | `value` From 0 to 1 or 1 to 100
+
+
+## Code sample
+
+```4d
+// Class _myDialog_Controller
+Class constructor
This.isSubform:=False
This.toBeInitialized:=False
// Instantiate the formDelegate
This.form:=cs.formDelegate.new(This)
+
+ ...
+
+ This.form.init()
+
+Function init()
+
+ This.listbox:=This.form.listbox.new("List Box")
+
+ ...
+
+ // The right edge of the lisbox must be in the middle (50%) of the dialog,
// & the width must be no less than 130px and no more than 520 px
This.form.constraints.add({target: This.listbox; set: [\
{type: "right"; value: 50}; \
{type: "minimum-width"; value: 150}; \
{type: "maximum-width"; value: 520}\
]})
+
// The "input" box must stay centered in the dialog.
// 📌 The "input.label" is automatically attached to the input box.
This.form.constraints.add({\
target: "input"; \
type: "horizontal-alignment"; \
alignment: "center"\
})
+
+ ...
+
+Function handleEvents($e : cs.evt)
$e:=$e || cs.evt.new()
If ($e.form)
// Mark: FORM METHOD
Case of
//==============================================
: ($e.load)
This.form.onLoad()
//==============================================
: ($e.resize)
// Applying the constraints
This.form.constraints.apply()
//==============================================
End case
Else
// Mark: WIDGETS METHOD
+
+ ...
End if
+
+Function onLoad()
+
+ ...
+
// Applying the constraints
This.form.constraints.apply()
+
+```
+
diff --git a/Documentation/Classes/formDelegate.md b/Documentation/Classes/formDelegate.md
index 12759a9..2d28383 100644
--- a/Documentation/Classes/formDelegate.md
+++ b/Documentation/Classes/formDelegate.md
@@ -43,6 +43,7 @@ Function update()
|**.isSubform** | Is the form used as a subform * |`Boolean`|**False**|X
|**.toBeInitialized** | Has the form been initialized * |`Boolean`|**True**|X
|**.window** | Current form window class object |`cs.windowDelegate`
+|**.constraints** | The constraints manager |`cs.constraintsDelegate`
> * To be set up by the dialog form class
diff --git a/Project/Sources/Classes/_DEMO_constraints_Controller.4dm b/Project/Sources/Classes/_DEMO_constraints_Controller.4dm
index 7839f77..7b033c6 100644
--- a/Project/Sources/Classes/_DEMO_constraints_Controller.4dm
+++ b/Project/Sources/Classes/_DEMO_constraints_Controller.4dm
@@ -40,8 +40,8 @@ Function init()
// The left panel should always be 50% of the window width
This:C1470.form.constraints.add({\
target: "left"; \
- type: "right"; \
- value: 50\
+ type: "tile"; \
+ value: 0.5\
})
// The right edge of the first hello world must be in the middle (50%),
diff --git a/Project/Sources/Classes/constraintsDelegate.4dm b/Project/Sources/Classes/constraintsDelegate.4dm
index c1e2793..be39a79 100644
--- a/Project/Sources/Classes/constraintsDelegate.4dm
+++ b/Project/Sources/Classes/constraintsDelegate.4dm
@@ -4,14 +4,37 @@ Class constructor($metrics : Object)
This:C1470.rules:=[]
+ If (OB Instance of:C1731($metrics; 4D:C1709.File))
+
+ This:C1470.load($metrics)
+
+ Else
+
+ This:C1470.setMetrics($metrics)
+
+ End if
+
+ This:C1470._matrix:=Not:C34(Is compiled mode:C492) // True if Dev mode
+
+ // === === === === === === === === === === === === === === === === === === === === === === === ===
+Function load($file : 4D:C1709.File)
+
+ var $metrics; $o : Object
+
+ $o:=JSON Parse:C1218($metrics.getText().rules)
+ This:C1470.rules:=$o.rules || []
+
+ This:C1470.setMetrics($o)
+
+ // === === === === === === === === === === === === === === === === === === === === === === === ===
+Function setMetrics($metrics : Object)
+
+ // TODO: Adjusting default values for Windows
This:C1470.scrollBarWidth:=$metrics.scrollBarWidth || Is macOS:C1572 ? 15 : 15
This:C1470.marginV:=$metrics.marginV || Is macOS:C1572 ? 2 : 2
This:C1470.marginH:=$metrics.marginH || Is macOS:C1572 ? 20 : 20
-
- This:C1470.labelMargin:=Is macOS:C1572 ? 10 : 10
- This:C1470.offset:=2
-
- This:C1470._matrix:=Not:C34(Is compiled mode:C492) // True if Dev mode
+ This:C1470.labelMargin:=$metrics.labelMargin || Is macOS:C1572 ? 10 : 10
+ This:C1470.offset:=$metrics.offset || Is macOS:C1572 ? 2 : 2
// === === === === === === === === === === === === === === === === === === === === === === === ===
Function add($rule : Object) : cs:C1710.constraintsDelegate
@@ -78,10 +101,18 @@ Function apply()
For each ($rule; This:C1470.rules)
- If ($rule.formula#Null:C1517)\
- && (OB Instance of:C1731($rule.formula; 4D:C1709.Function))
+ If ($rule.formula#Null:C1517)
+
+ If (OB Instance of:C1731($rule.formula; 4D:C1709.Function))
+
+ $rule.formula()
+
+ Else
+
+ Formula from string:C1601(String:C10($rule.formula)).call(Null:C1517)
+
+ End if
- $rule.formula.call(Null:C1517)
continue
End if
@@ -434,7 +465,9 @@ Function apply()
// MARK:tile
// Calculate proportional width
- $width:=Int:C8(($ref.width)*Num:C11($rule.value))
+ $width:=Int:C8(($ref.width)*($rule.value<1 ? $rule.value : $rule.value/100))
+
+ //$width:=$ref.width*($rule.value<1 ? $rule.value : $rule.value/100)
If ($rule.parent#Null:C1517)
diff --git a/Project/Sources/Classes/formDelegate.4dm b/Project/Sources/Classes/formDelegate.4dm
index dbc5654..b16dfaf 100644
--- a/Project/Sources/Classes/formDelegate.4dm
+++ b/Project/Sources/Classes/formDelegate.4dm
@@ -2,6 +2,9 @@ property isSubform; toBeInitialized : Boolean
property pages : Object
property entryOrder; _instantiableWidgets; _mapEvents : Collection
+property window : cs:C1710.windowDelegate
+property constraints : cs:C1710.constraintsDelegate
+
property __CLASS__ : Object
property __DELEGATES__ : Collection
diff --git a/Project/Sources/Forms/DEMO_constraints/form.4DForm b/Project/Sources/Forms/DEMO_constraints/form.4DForm
index 0aa4816..bb97b71 100644
--- a/Project/Sources/Forms/DEMO_constraints/form.4DForm
+++ b/Project/Sources/Forms/DEMO_constraints/form.4DForm
@@ -387,7 +387,7 @@
}
}
],
- "geometryStamp": 899,
+ "geometryStamp": 907,
"editor": {
"activeView": "View 1",
"defaultView": "View 1",
diff --git a/Resources/en.lproj/syntaxEN.json b/Resources/en.lproj/syntaxEN.json
index 18936e6..0047b98 100644
--- a/Resources/en.lproj/syntaxEN.json
+++ b/Resources/en.lproj/syntaxEN.json
@@ -2480,6 +2480,17 @@
"Params": [],
"Summary": ""
},
+ "load()": {
+ "Syntax": "**.load**( *file* : 4D.File )",
+ "Params": [
+ [
+ "file",
+ "4D.File",
+ "->"
+ ]
+ ],
+ "Summary": ""
+ },
"add()": {
"Syntax": "**.add**( *rule* : Object ) : cs.ui.constraintsDelegate",
"Params": [
@@ -2496,6 +2507,17 @@
],
"Summary": ""
},
+ "setMetrics()": {
+ "Syntax": "**.setMetrics**( *metrics* : Object )",
+ "Params": [
+ [
+ "metrics",
+ "Object",
+ "->"
+ ]
+ ],
+ "Summary": ""
+ },
"rules": {
"Syntax": "rules : Collection"
}
@@ -5169,6 +5191,9 @@
"Params": [],
"Summary": ""
},
+ "window": {
+ "Syntax": "window : cs.ui.windowDelegate"
+ },
"entryOrder": {
"Syntax": "entryOrder : Collection"
},
@@ -5178,6 +5203,9 @@
"toBeInitialized": {
"Syntax": "toBeInitialized : Boolean"
},
+ "constraints": {
+ "Syntax": "constraints : cs.ui.constraintsDelegate"
+ },
"isSubform": {
"Syntax": "isSubform : Boolean"
}