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" }