11
11
TYPE_CHECKING ,
12
12
Any ,
13
13
Collection ,
14
+ Literal ,
14
15
NamedTuple ,
15
16
Tuple ,
16
17
TypeVar ,
@@ -986,20 +987,23 @@ def inflect(
986
987
A positive value will move the region right or down, a negative value will move
987
988
the region left or up. A value of `0` will leave that axis unmodified.
988
989
990
+ If a margin is provided, it will add space between the resulting region.
991
+
992
+ Note that if margin is specified it *overlaps*, so the space will be the maximum
993
+ of two edges, and not the total.
994
+
989
995
```
990
996
╔══════════╗ │
991
997
║ ║
992
998
║ Self ║ │
993
999
║ ║
994
1000
╚══════════╝ │
995
1001
996
- ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─
997
-
998
- │ ┌──────────┐
999
- │ │
1000
- │ │ Result │
1001
- │ │
1002
- │ └──────────┘
1002
+ ─ ─ ─ ─ ─ ─ ─ ─ ┌──────────┐
1003
+ │ │
1004
+ │ Result │
1005
+ │ │
1006
+ └──────────┘
1003
1007
```
1004
1008
1005
1009
Args:
@@ -1013,11 +1017,89 @@ def inflect(
1013
1017
inflect_margin = NULL_SPACING if margin is None else margin
1014
1018
x , y , width , height = self
1015
1019
if x_axis :
1016
- x += (width + inflect_margin .width ) * x_axis
1020
+ x += (width + inflect_margin .max_width ) * x_axis
1017
1021
if y_axis :
1018
- y += (height + inflect_margin .height ) * y_axis
1022
+ y += (height + inflect_margin .max_height ) * y_axis
1019
1023
return Region (x , y , width , height )
1020
1024
1025
+ def constrain (
1026
+ self ,
1027
+ constrain_x : Literal ["none" , "inside" , "inflect" ],
1028
+ constrain_y : Literal ["none" , "inside" , "inflect" ],
1029
+ margin : Spacing ,
1030
+ container : Region ,
1031
+ ) -> Region :
1032
+ """Constrain a region to fit within a container, using different methods per axis.
1033
+
1034
+ Args:
1035
+ constrain_x: Constrain method for the X-axis.
1036
+ constrain_y: Constrain method for the Y-axis.
1037
+ margin: Margin to maintain around region.
1038
+ container: Container to constrain to.
1039
+
1040
+ Returns:
1041
+ New widget, that fits inside the container (if possible).
1042
+ """
1043
+ margin_region = self .grow (margin )
1044
+ region = self
1045
+
1046
+ def compare_span (
1047
+ span_start : int , span_end : int , container_start : int , container_end : int
1048
+ ) -> int :
1049
+ """Compare a span with a container
1050
+
1051
+ Args:
1052
+ span_start: Start of the span.
1053
+ span_end: end of the span.
1054
+ container_start: Start of the container.
1055
+ container_end: End of the container.
1056
+
1057
+ Returns:
1058
+ 0 if the span fits, -1 if it is less that the container, otherwise +1
1059
+ """
1060
+ if span_start >= container_start and span_end <= container_end :
1061
+ return 0
1062
+ if span_start < container_start :
1063
+ return - 1
1064
+ return + 1
1065
+
1066
+ # Apply any inflected constraints
1067
+ if constrain_x == "inflect" or constrain_y == "inflect" :
1068
+ region = region .inflect (
1069
+ (
1070
+ - compare_span (
1071
+ margin_region .x ,
1072
+ margin_region .right ,
1073
+ container .x ,
1074
+ container .right ,
1075
+ )
1076
+ if constrain_x == "inflect"
1077
+ else 0
1078
+ ),
1079
+ (
1080
+ - compare_span (
1081
+ margin_region .y ,
1082
+ margin_region .bottom ,
1083
+ container .y ,
1084
+ container .bottom ,
1085
+ )
1086
+ if constrain_y == "inflect"
1087
+ else 0
1088
+ ),
1089
+ margin ,
1090
+ )
1091
+
1092
+ # Apply translate inside constrains
1093
+ # Note this is also applied, if a previous inflect constrained has been applied
1094
+ # This is so that the origin is always inside the container
1095
+ region = region .translate_inside (
1096
+ container .shrink (margin ),
1097
+ constrain_x != "none" ,
1098
+ constrain_y != "none" ,
1099
+ )
1100
+
1101
+ return region
1102
+
1021
1103
1022
1104
class Spacing (NamedTuple ):
1023
1105
"""Stores spacing around a widget, such as padding and border.
@@ -1072,6 +1154,18 @@ def height(self) -> int:
1072
1154
"""Total space in the y axis."""
1073
1155
return self .top + self .bottom
1074
1156
1157
+ @property
1158
+ def max_width (self ) -> int :
1159
+ """The space between regions in the X direction if margins overlap, i.e. `max(self.left, self.right)`."""
1160
+ _top , right , _bottom , left = self
1161
+ return left if left > right else right
1162
+
1163
+ @property
1164
+ def max_height (self ) -> int :
1165
+ """The space between regions in the Y direction if margins overlap, i.e. `max(self.top, self.bottom)`."""
1166
+ top , _right , bottom , _left = self
1167
+ return top if top > bottom else bottom
1168
+
1075
1169
@property
1076
1170
def top_left (self ) -> tuple [int , int ]:
1077
1171
"""A pair of integers for the left, and top space."""
0 commit comments