urwid 2.6.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of urwid might be problematic. Click here for more details.
- urwid/__init__.py +333 -0
- urwid/canvas.py +1413 -0
- urwid/command_map.py +137 -0
- urwid/container.py +59 -0
- urwid/decoration.py +65 -0
- urwid/display/__init__.py +97 -0
- urwid/display/_posix_raw_display.py +413 -0
- urwid/display/_raw_display_base.py +914 -0
- urwid/display/_web.css +12 -0
- urwid/display/_web.js +462 -0
- urwid/display/_win32.py +171 -0
- urwid/display/_win32_raw_display.py +269 -0
- urwid/display/common.py +1219 -0
- urwid/display/curses.py +690 -0
- urwid/display/escape.py +624 -0
- urwid/display/html_fragment.py +251 -0
- urwid/display/lcd.py +518 -0
- urwid/display/raw.py +37 -0
- urwid/display/web.py +636 -0
- urwid/event_loop/__init__.py +55 -0
- urwid/event_loop/abstract_loop.py +175 -0
- urwid/event_loop/asyncio_loop.py +231 -0
- urwid/event_loop/glib_loop.py +294 -0
- urwid/event_loop/main_loop.py +721 -0
- urwid/event_loop/select_loop.py +230 -0
- urwid/event_loop/tornado_loop.py +206 -0
- urwid/event_loop/trio_loop.py +302 -0
- urwid/event_loop/twisted_loop.py +269 -0
- urwid/event_loop/zmq_loop.py +275 -0
- urwid/font.py +695 -0
- urwid/graphics.py +96 -0
- urwid/highlight.css +19 -0
- urwid/listbox.py +1899 -0
- urwid/monitored_list.py +522 -0
- urwid/numedit.py +376 -0
- urwid/signals.py +330 -0
- urwid/split_repr.py +130 -0
- urwid/str_util.py +358 -0
- urwid/text_layout.py +632 -0
- urwid/treetools.py +515 -0
- urwid/util.py +557 -0
- urwid/version.py +16 -0
- urwid/vterm.py +1806 -0
- urwid/widget/__init__.py +181 -0
- urwid/widget/attr_map.py +161 -0
- urwid/widget/attr_wrap.py +140 -0
- urwid/widget/bar_graph.py +649 -0
- urwid/widget/big_text.py +77 -0
- urwid/widget/box_adapter.py +126 -0
- urwid/widget/columns.py +1145 -0
- urwid/widget/constants.py +574 -0
- urwid/widget/container.py +227 -0
- urwid/widget/divider.py +110 -0
- urwid/widget/edit.py +718 -0
- urwid/widget/filler.py +403 -0
- urwid/widget/frame.py +539 -0
- urwid/widget/grid_flow.py +539 -0
- urwid/widget/line_box.py +194 -0
- urwid/widget/overlay.py +829 -0
- urwid/widget/padding.py +597 -0
- urwid/widget/pile.py +971 -0
- urwid/widget/popup.py +170 -0
- urwid/widget/progress_bar.py +141 -0
- urwid/widget/scrollable.py +597 -0
- urwid/widget/solid_fill.py +44 -0
- urwid/widget/text.py +354 -0
- urwid/widget/widget.py +852 -0
- urwid/widget/widget_decoration.py +166 -0
- urwid/widget/wimp.py +792 -0
- urwid/wimp.py +23 -0
- urwid-2.6.0.post0.dist-info/COPYING +504 -0
- urwid-2.6.0.post0.dist-info/METADATA +332 -0
- urwid-2.6.0.post0.dist-info/RECORD +75 -0
- urwid-2.6.0.post0.dist-info/WHEEL +5 -0
- urwid-2.6.0.post0.dist-info/top_level.txt +1 -0
urwid/widget/filler.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from urwid.canvas import CompositeCanvas
|
|
7
|
+
from urwid.split_repr import remove_defaults
|
|
8
|
+
from urwid.util import int_scale
|
|
9
|
+
|
|
10
|
+
from .constants import (
|
|
11
|
+
RELATIVE_100,
|
|
12
|
+
Sizing,
|
|
13
|
+
VAlign,
|
|
14
|
+
WHSettings,
|
|
15
|
+
normalize_height,
|
|
16
|
+
normalize_valign,
|
|
17
|
+
simplify_height,
|
|
18
|
+
simplify_valign,
|
|
19
|
+
)
|
|
20
|
+
from .widget_decoration import WidgetDecoration, WidgetError
|
|
21
|
+
|
|
22
|
+
if typing.TYPE_CHECKING:
|
|
23
|
+
from typing_extensions import Literal
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
WrappedWidget = typing.TypeVar("WrappedWidget")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FillerError(WidgetError):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Filler(WidgetDecoration[WrappedWidget]):
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
body: WrappedWidget,
|
|
37
|
+
valign: (
|
|
38
|
+
Literal["top", "middle", "bottom"] | VAlign | tuple[Literal["relative", WHSettings.RELATIVE], int]
|
|
39
|
+
) = VAlign.MIDDLE,
|
|
40
|
+
height: (
|
|
41
|
+
int | Literal["pack", WHSettings.PACK] | tuple[Literal["relative", WHSettings.RELATIVE], int] | None
|
|
42
|
+
) = WHSettings.PACK,
|
|
43
|
+
min_height: int | None = None,
|
|
44
|
+
top: int = 0,
|
|
45
|
+
bottom: int = 0,
|
|
46
|
+
) -> None:
|
|
47
|
+
"""
|
|
48
|
+
:param body: a flow widget or box widget to be filled around (stored as self.original_widget)
|
|
49
|
+
:type body: Widget
|
|
50
|
+
|
|
51
|
+
:param valign: one of:
|
|
52
|
+
``'top'``, ``'middle'``, ``'bottom'``,
|
|
53
|
+
(``'relative'``, *percentage* 0=top 100=bottom)
|
|
54
|
+
|
|
55
|
+
:param height: one of:
|
|
56
|
+
|
|
57
|
+
``'pack'``
|
|
58
|
+
if body is a flow widget
|
|
59
|
+
|
|
60
|
+
*given height*
|
|
61
|
+
integer number of rows for self.original_widget
|
|
62
|
+
|
|
63
|
+
(``'relative'``, *percentage of total height*)
|
|
64
|
+
make height depend on container's height
|
|
65
|
+
|
|
66
|
+
:param min_height: one of:
|
|
67
|
+
|
|
68
|
+
``None``
|
|
69
|
+
if no minimum or if body is a flow widget
|
|
70
|
+
|
|
71
|
+
*minimum height*
|
|
72
|
+
integer number of rows for the widget when height not fixed
|
|
73
|
+
|
|
74
|
+
:param top: a fixed number of rows to fill at the top
|
|
75
|
+
:type top: int
|
|
76
|
+
:param bottom: a fixed number of rows to fill at the bottom
|
|
77
|
+
:type bottom: int
|
|
78
|
+
|
|
79
|
+
If body is a flow widget, then height must be ``'pack'`` and *min_height* will be ignored.
|
|
80
|
+
Sizing of the filler will be BOX/FLOW in this case.
|
|
81
|
+
|
|
82
|
+
If height is integer, *min_height* will be ignored and sizing of filler will be BOX/FLOW.
|
|
83
|
+
|
|
84
|
+
Filler widgets will try to satisfy height argument first by reducing the valign amount when necessary.
|
|
85
|
+
If height still cannot be satisfied it will also be reduced.
|
|
86
|
+
"""
|
|
87
|
+
super().__init__(body)
|
|
88
|
+
|
|
89
|
+
# convert old parameters to the new top/bottom values
|
|
90
|
+
if isinstance(height, tuple):
|
|
91
|
+
if height[0] == "fixed top":
|
|
92
|
+
if not isinstance(valign, tuple) or valign[0] != "fixed bottom":
|
|
93
|
+
raise FillerError("fixed top height may only be used with fixed bottom valign")
|
|
94
|
+
top = height[1]
|
|
95
|
+
height = RELATIVE_100
|
|
96
|
+
elif height[0] == "fixed bottom":
|
|
97
|
+
if not isinstance(valign, tuple) or valign[0] != "fixed top":
|
|
98
|
+
raise FillerError("fixed bottom height may only be used with fixed top valign")
|
|
99
|
+
bottom = height[1]
|
|
100
|
+
height = RELATIVE_100
|
|
101
|
+
|
|
102
|
+
if isinstance(valign, tuple):
|
|
103
|
+
if valign[0] == "fixed top":
|
|
104
|
+
top = valign[1]
|
|
105
|
+
normalized_valign = VAlign.TOP
|
|
106
|
+
elif valign[0] == "fixed bottom":
|
|
107
|
+
bottom = valign[1]
|
|
108
|
+
normalized_valign = VAlign.BOTTOM
|
|
109
|
+
else:
|
|
110
|
+
normalized_valign = valign
|
|
111
|
+
|
|
112
|
+
elif not isinstance(valign, (VAlign, str)):
|
|
113
|
+
raise FillerError(f"invalid valign: {valign!r}")
|
|
114
|
+
|
|
115
|
+
else:
|
|
116
|
+
normalized_valign = VAlign(valign)
|
|
117
|
+
|
|
118
|
+
# convert old flow mode parameter height=None to height='flow'
|
|
119
|
+
if height is None or height == Sizing.FLOW:
|
|
120
|
+
height = WHSettings.PACK
|
|
121
|
+
|
|
122
|
+
self.top = top
|
|
123
|
+
self.bottom = bottom
|
|
124
|
+
self.valign_type, self.valign_amount = normalize_valign(normalized_valign, FillerError)
|
|
125
|
+
self.height_type, self.height_amount = normalize_height(height, FillerError)
|
|
126
|
+
|
|
127
|
+
if self.height_type not in {WHSettings.GIVEN, WHSettings.PACK}:
|
|
128
|
+
self.min_height = min_height
|
|
129
|
+
else:
|
|
130
|
+
self.min_height = None
|
|
131
|
+
|
|
132
|
+
def sizing(self) -> frozenset[Sizing]:
|
|
133
|
+
"""Widget sizing.
|
|
134
|
+
|
|
135
|
+
Sizing BOX is always supported.
|
|
136
|
+
Sizing FLOW is supported if: FLOW widget (a height type is PACK) or BOX widget with height GIVEN
|
|
137
|
+
"""
|
|
138
|
+
sizing: set[Sizing] = {Sizing.BOX}
|
|
139
|
+
if self.height_type in {WHSettings.PACK, WHSettings.GIVEN}:
|
|
140
|
+
sizing.add(Sizing.FLOW)
|
|
141
|
+
return frozenset(sizing)
|
|
142
|
+
|
|
143
|
+
def rows(self, size: tuple[int], focus: bool = False) -> int:
|
|
144
|
+
"""Flow pack support if FLOW sizing supported."""
|
|
145
|
+
if self.height_type == WHSettings.PACK:
|
|
146
|
+
return self.original_widget.rows(size, focus) + self.top + self.bottom
|
|
147
|
+
if self.height_type == WHSettings.GIVEN:
|
|
148
|
+
return self.height_amount + self.top + self.bottom
|
|
149
|
+
raise FillerError("Method 'rows' not supported for BOX widgets") # pragma: no cover
|
|
150
|
+
|
|
151
|
+
def _repr_attrs(self) -> dict[str, typing.Any]:
|
|
152
|
+
attrs = {
|
|
153
|
+
**super()._repr_attrs(),
|
|
154
|
+
"valign": simplify_valign(self.valign_type, self.valign_amount),
|
|
155
|
+
"height": simplify_height(self.height_type, self.height_amount),
|
|
156
|
+
"top": self.top,
|
|
157
|
+
"bottom": self.bottom,
|
|
158
|
+
"min_height": self.min_height,
|
|
159
|
+
}
|
|
160
|
+
return remove_defaults(attrs, Filler.__init__)
|
|
161
|
+
|
|
162
|
+
@property
|
|
163
|
+
def body(self) -> WrappedWidget:
|
|
164
|
+
"""backwards compatibility, widget used to be stored as body"""
|
|
165
|
+
warnings.warn(
|
|
166
|
+
"backwards compatibility, widget used to be stored as body",
|
|
167
|
+
PendingDeprecationWarning,
|
|
168
|
+
stacklevel=2,
|
|
169
|
+
)
|
|
170
|
+
return self.original_widget
|
|
171
|
+
|
|
172
|
+
@body.setter
|
|
173
|
+
def body(self, new_body: WrappedWidget) -> None:
|
|
174
|
+
warnings.warn(
|
|
175
|
+
"backwards compatibility, widget used to be stored as body",
|
|
176
|
+
PendingDeprecationWarning,
|
|
177
|
+
stacklevel=2,
|
|
178
|
+
)
|
|
179
|
+
self.original_widget = new_body
|
|
180
|
+
|
|
181
|
+
def get_body(self) -> WrappedWidget:
|
|
182
|
+
"""backwards compatibility, widget used to be stored as body"""
|
|
183
|
+
warnings.warn(
|
|
184
|
+
"backwards compatibility, widget used to be stored as body",
|
|
185
|
+
DeprecationWarning,
|
|
186
|
+
stacklevel=2,
|
|
187
|
+
)
|
|
188
|
+
return self.original_widget
|
|
189
|
+
|
|
190
|
+
def set_body(self, new_body: WrappedWidget) -> None:
|
|
191
|
+
warnings.warn(
|
|
192
|
+
"backwards compatibility, widget used to be stored as body",
|
|
193
|
+
DeprecationWarning,
|
|
194
|
+
stacklevel=2,
|
|
195
|
+
)
|
|
196
|
+
self.original_widget = new_body
|
|
197
|
+
|
|
198
|
+
def selectable(self) -> bool:
|
|
199
|
+
"""Return selectable from body."""
|
|
200
|
+
return self._original_widget.selectable()
|
|
201
|
+
|
|
202
|
+
def filler_values(self, size: tuple[int, int] | tuple[int], focus: bool) -> tuple[int, int]:
|
|
203
|
+
"""
|
|
204
|
+
Return the number of rows to pad on the top and bottom.
|
|
205
|
+
|
|
206
|
+
Override this method to define custom padding behaviour.
|
|
207
|
+
"""
|
|
208
|
+
maxcol, maxrow = self.pack(size, focus)
|
|
209
|
+
|
|
210
|
+
if self.height_type == WHSettings.PACK:
|
|
211
|
+
height = self._original_widget.rows((maxcol,), focus=focus)
|
|
212
|
+
return calculate_top_bottom_filler(
|
|
213
|
+
maxrow,
|
|
214
|
+
self.valign_type,
|
|
215
|
+
self.valign_amount,
|
|
216
|
+
WHSettings.GIVEN,
|
|
217
|
+
height,
|
|
218
|
+
None,
|
|
219
|
+
self.top,
|
|
220
|
+
self.bottom,
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
return calculate_top_bottom_filler(
|
|
224
|
+
maxrow,
|
|
225
|
+
self.valign_type,
|
|
226
|
+
self.valign_amount,
|
|
227
|
+
self.height_type,
|
|
228
|
+
self.height_amount,
|
|
229
|
+
self.min_height,
|
|
230
|
+
self.top,
|
|
231
|
+
self.bottom,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def render(self, size: tuple[int, int] | tuple[int], focus: bool = False) -> CompositeCanvas:
|
|
235
|
+
"""Render self.original_widget with space above and/or below."""
|
|
236
|
+
maxcol, maxrow = self.pack(size, focus)
|
|
237
|
+
top, bottom = self.filler_values(size, focus)
|
|
238
|
+
|
|
239
|
+
if self.height_type == WHSettings.PACK:
|
|
240
|
+
canv = self._original_widget.render((maxcol,), focus)
|
|
241
|
+
else:
|
|
242
|
+
canv = self._original_widget.render((maxcol, maxrow - top - bottom), focus)
|
|
243
|
+
canv = CompositeCanvas(canv)
|
|
244
|
+
|
|
245
|
+
if maxrow and canv.rows() > maxrow and canv.cursor is not None:
|
|
246
|
+
_cx, cy = canv.cursor
|
|
247
|
+
if cy >= maxrow:
|
|
248
|
+
canv.trim(cy - maxrow + 1, maxrow - top - bottom)
|
|
249
|
+
if canv.rows() > maxrow:
|
|
250
|
+
canv.trim(0, maxrow)
|
|
251
|
+
return canv
|
|
252
|
+
canv.pad_trim_top_bottom(top, bottom)
|
|
253
|
+
return canv
|
|
254
|
+
|
|
255
|
+
def keypress(self, size: tuple[int, int] | tuple[()], key: str) -> str | None:
|
|
256
|
+
"""Pass keypress to self.original_widget."""
|
|
257
|
+
maxcol, maxrow = self.pack(size, True)
|
|
258
|
+
if self.height_type == WHSettings.PACK:
|
|
259
|
+
return self._original_widget.keypress((maxcol,), key)
|
|
260
|
+
|
|
261
|
+
top, bottom = self.filler_values((maxcol, maxrow), True)
|
|
262
|
+
return self._original_widget.keypress((maxcol, maxrow - top - bottom), key)
|
|
263
|
+
|
|
264
|
+
def get_cursor_coords(self, size: tuple[int, int] | tuple[int]) -> tuple[int, int] | None:
|
|
265
|
+
"""Return cursor coords from self.original_widget if any."""
|
|
266
|
+
maxcol, maxrow = self.pack(size, True)
|
|
267
|
+
if not hasattr(self._original_widget, "get_cursor_coords"):
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
top, bottom = self.filler_values(size, True)
|
|
271
|
+
if self.height_type == WHSettings.PACK:
|
|
272
|
+
coords = self._original_widget.get_cursor_coords((maxcol,))
|
|
273
|
+
else:
|
|
274
|
+
coords = self._original_widget.get_cursor_coords((maxcol, maxrow - top - bottom))
|
|
275
|
+
if not coords:
|
|
276
|
+
return None
|
|
277
|
+
x, y = coords
|
|
278
|
+
if y >= maxrow:
|
|
279
|
+
y = maxrow - 1
|
|
280
|
+
return x, y + top
|
|
281
|
+
|
|
282
|
+
def get_pref_col(self, size: tuple[int, int] | tuple[int]) -> int | None:
|
|
283
|
+
"""Return pref_col from self.original_widget if any."""
|
|
284
|
+
maxcol, maxrow = self.pack(size, True)
|
|
285
|
+
if not hasattr(self._original_widget, "get_pref_col"):
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
if self.height_type == WHSettings.PACK:
|
|
289
|
+
x = self._original_widget.get_pref_col((maxcol,))
|
|
290
|
+
else:
|
|
291
|
+
top, bottom = self.filler_values(size, True)
|
|
292
|
+
x = self._original_widget.get_pref_col((maxcol, maxrow - top - bottom))
|
|
293
|
+
|
|
294
|
+
return x
|
|
295
|
+
|
|
296
|
+
def move_cursor_to_coords(self, size: tuple[int, int] | tuple[int], col: int, row: int) -> bool:
|
|
297
|
+
"""Pass to self.original_widget."""
|
|
298
|
+
maxcol, maxrow = self.pack(size, True)
|
|
299
|
+
if not hasattr(self._original_widget, "move_cursor_to_coords"):
|
|
300
|
+
return True
|
|
301
|
+
|
|
302
|
+
top, bottom = self.filler_values(size, True)
|
|
303
|
+
if row < top or row >= maxcol - bottom:
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
if self.height_type == WHSettings.PACK:
|
|
307
|
+
return self._original_widget.move_cursor_to_coords((maxcol,), col, row - top)
|
|
308
|
+
return self._original_widget.move_cursor_to_coords((maxcol, maxrow - top - bottom), col, row - top)
|
|
309
|
+
|
|
310
|
+
def mouse_event(
|
|
311
|
+
self,
|
|
312
|
+
size: tuple[int, int] | tuple[int],
|
|
313
|
+
event,
|
|
314
|
+
button: int,
|
|
315
|
+
col: int,
|
|
316
|
+
row: int,
|
|
317
|
+
focus: bool,
|
|
318
|
+
) -> bool | None:
|
|
319
|
+
"""Pass to self.original_widget."""
|
|
320
|
+
maxcol, maxrow = self.pack(size, focus)
|
|
321
|
+
if not hasattr(self._original_widget, "mouse_event"):
|
|
322
|
+
return False
|
|
323
|
+
|
|
324
|
+
top, bottom = self.filler_values(size, True)
|
|
325
|
+
if row < top or row >= maxrow - bottom:
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
if self.height_type == WHSettings.PACK:
|
|
329
|
+
return self._original_widget.mouse_event((maxcol,), event, button, col, row - top, focus)
|
|
330
|
+
return self._original_widget.mouse_event((maxcol, maxrow - top - bottom), event, button, col, row - top, focus)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def calculate_top_bottom_filler(
|
|
334
|
+
maxrow: int,
|
|
335
|
+
valign_type: Literal["top", "middle", "bottom", "relative", WHSettings.RELATIVE] | VAlign,
|
|
336
|
+
valign_amount: int,
|
|
337
|
+
height_type: Literal["given", "relative", "clip", WHSettings.GIVEN, WHSettings.RELATIVE, WHSettings.CLIP],
|
|
338
|
+
height_amount: int,
|
|
339
|
+
min_height: int | None,
|
|
340
|
+
top: int,
|
|
341
|
+
bottom: int,
|
|
342
|
+
) -> tuple[int, int]:
|
|
343
|
+
"""
|
|
344
|
+
Return the amount of filler (or clipping) on the top and
|
|
345
|
+
bottom part of maxrow rows to satisfy the following:
|
|
346
|
+
|
|
347
|
+
valign_type -- 'top', 'middle', 'bottom', 'relative'
|
|
348
|
+
valign_amount -- a percentage when align_type=='relative'
|
|
349
|
+
height_type -- 'given', 'relative', 'clip'
|
|
350
|
+
height_amount -- a percentage when width_type=='relative'
|
|
351
|
+
otherwise equal to the height of the widget
|
|
352
|
+
min_height -- a desired minimum width for the widget or None
|
|
353
|
+
top -- a fixed number of rows to fill on the top
|
|
354
|
+
bottom -- a fixed number of rows to fill on the bottom
|
|
355
|
+
|
|
356
|
+
>>> ctbf = calculate_top_bottom_filler
|
|
357
|
+
>>> ctbf(15, 'top', 0, 'given', 10, None, 2, 0)
|
|
358
|
+
(2, 3)
|
|
359
|
+
>>> ctbf(15, 'relative', 0, 'given', 10, None, 2, 0)
|
|
360
|
+
(2, 3)
|
|
361
|
+
>>> ctbf(15, 'relative', 100, 'given', 10, None, 2, 0)
|
|
362
|
+
(5, 0)
|
|
363
|
+
>>> ctbf(15, 'middle', 0, 'given', 4, None, 2, 0)
|
|
364
|
+
(6, 5)
|
|
365
|
+
>>> ctbf(15, 'middle', 0, 'given', 18, None, 2, 0)
|
|
366
|
+
(0, 0)
|
|
367
|
+
>>> ctbf(20, 'top', 0, 'relative', 60, None, 0, 0)
|
|
368
|
+
(0, 8)
|
|
369
|
+
>>> ctbf(20, 'relative', 30, 'relative', 60, None, 0, 0)
|
|
370
|
+
(2, 6)
|
|
371
|
+
>>> ctbf(20, 'relative', 30, 'relative', 60, 14, 0, 0)
|
|
372
|
+
(2, 4)
|
|
373
|
+
"""
|
|
374
|
+
if height_type == WHSettings.RELATIVE:
|
|
375
|
+
maxheight = max(maxrow - top - bottom, 0)
|
|
376
|
+
height = int_scale(height_amount, 101, maxheight + 1)
|
|
377
|
+
if min_height is not None:
|
|
378
|
+
height = max(height, min_height)
|
|
379
|
+
else:
|
|
380
|
+
height = height_amount
|
|
381
|
+
|
|
382
|
+
valign = {VAlign.TOP: 0, VAlign.MIDDLE: 50, VAlign.BOTTOM: 100}.get(valign_type, valign_amount)
|
|
383
|
+
|
|
384
|
+
# add the remainder of top/bottom to the filler
|
|
385
|
+
filler = maxrow - height - top - bottom
|
|
386
|
+
bottom += int_scale(100 - valign, 101, filler + 1)
|
|
387
|
+
top = maxrow - height - bottom
|
|
388
|
+
|
|
389
|
+
# reduce filler if we are clipping an edge
|
|
390
|
+
if bottom < 0 < top:
|
|
391
|
+
shift = min(top, -bottom)
|
|
392
|
+
top -= shift
|
|
393
|
+
bottom += shift
|
|
394
|
+
elif top < 0 < bottom:
|
|
395
|
+
shift = min(bottom, -top)
|
|
396
|
+
bottom -= shift
|
|
397
|
+
top += shift
|
|
398
|
+
|
|
399
|
+
# no negative values for filler at the moment
|
|
400
|
+
top = max(top, 0)
|
|
401
|
+
bottom = max(bottom, 0)
|
|
402
|
+
|
|
403
|
+
return top, bottom
|