responds-forms 0.4__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.
- cartas/__init__.py +2060 -0
- cartas/origin.py +1196 -0
- pyflutter_ui/__init__.py +30 -0
- pyflutter_ui/app.py +289 -0
- pyflutter_ui/file_picker.py +274 -0
- pyflutter_ui/navigator.py +93 -0
- pyflutter_ui/scheduler.py +154 -0
- pyflutter_ui/theme.py +1058 -0
- pyflutter_ui/ui/__init__.py +27 -0
- pyflutter_ui/ui/buttons.py +219 -0
- pyflutter_ui/ui/container.py +119 -0
- pyflutter_ui/ui/core.py +820 -0
- pyflutter_ui/ui/list_builder.py +396 -0
- pyflutter_ui/ui/progress.py +80 -0
- pyflutter_ui/ui/tables.py +109 -0
- pyflutter_ui/ui/widgets.py +1418 -0
- responds_forms-0.4.dist-info/METADATA +15 -0
- responds_forms-0.4.dist-info/RECORD +38 -0
- responds_forms-0.4.dist-info/WHEEL +4 -0
- responds_ui/__init__.py +1 -0
- responds_ui/__main__.py +25 -0
- responds_ui/assets/forms_config.json +57 -0
- responds_ui/config_app.py +109 -0
- responds_ui/lib_toi.py +864 -0
- responds_ui/page_forms/Diagnorstico.txt +108 -0
- responds_ui/page_forms/__init__.py +5 -0
- responds_ui/page_forms/common.py +192 -0
- responds_ui/page_forms/config.py +68 -0
- responds_ui/page_forms/controller_forms.py +390 -0
- responds_ui/page_forms/diagnose_forms.py +64 -0
- responds_ui/page_forms/master_forms.py +113 -0
- responds_ui/page_forms/model_forms.py +329 -0
- responds_ui/page_forms/service.py +347 -0
- responds_ui/page_forms/view_forms.py +164 -0
- responds_ui/table_widget.py +140 -0
- responds_ui/util.py +74 -0
- responds_ui/version.py +2 -0
- whatsapp_files/__init__.py +1092 -0
pyflutter_ui/ui/core.py
ADDED
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
# core.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
import tkinter as tk
|
|
4
|
+
from abc import ABC, abstractmethod, ABCMeta
|
|
5
|
+
from typing import Any, Callable, Optional, Type, TypeVar, cast, Union
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
WIDGET_TK = TypeVar("WIDGET_TK", bound="tk.Widget")
|
|
9
|
+
WIDGET_FLUTTER = TypeVar("WIDGET_FLUTTER", bound="FlutterWidget")
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
K = TypeVar('K')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def widget_exists(w: tk.Widget, *, destroy: bool = False) -> bool:
|
|
15
|
+
e = w.winfo_exists()
|
|
16
|
+
if destroy:
|
|
17
|
+
w.destroy()
|
|
18
|
+
return e
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class GlobalKey(object):
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.element: Element | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Disposable(ABC):
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
super().__init__()
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def dispose(self) -> None:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ============================================================
|
|
39
|
+
# STATE - CHANGE NOTIFIER
|
|
40
|
+
# ============================================================
|
|
41
|
+
class ChangeNotifier(object):
|
|
42
|
+
|
|
43
|
+
def __init__(self) -> None:
|
|
44
|
+
super().__init__()
|
|
45
|
+
self._listeners: list[Callable[[], None]] = []
|
|
46
|
+
|
|
47
|
+
def add_listener(self, listener: Callable[[], None]) -> None:
|
|
48
|
+
if listener not in self._listeners:
|
|
49
|
+
self._listeners.append(listener)
|
|
50
|
+
|
|
51
|
+
def remove_listener(self, listener: Callable[[], None]) -> None:
|
|
52
|
+
if listener in self._listeners:
|
|
53
|
+
self._listeners.remove(listener)
|
|
54
|
+
|
|
55
|
+
def notify_listeners(self) -> None:
|
|
56
|
+
for listener in self._listeners.copy():
|
|
57
|
+
if listener not in self._listeners:
|
|
58
|
+
continue # foi removido durante este mesmo ciclo — não chamar
|
|
59
|
+
|
|
60
|
+
listener()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ValueNotifier[T](ChangeNotifier):
|
|
64
|
+
|
|
65
|
+
def __init__(self, value: T) -> None:
|
|
66
|
+
super().__init__()
|
|
67
|
+
self._value: T = value
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def value(self) -> T:
|
|
71
|
+
return self._value
|
|
72
|
+
|
|
73
|
+
@value.setter
|
|
74
|
+
def value(self, value: T) -> None:
|
|
75
|
+
self._value = value
|
|
76
|
+
self.notify_listeners()
|
|
77
|
+
|
|
78
|
+
def get_value(self) -> T:
|
|
79
|
+
return self._value
|
|
80
|
+
|
|
81
|
+
def set_value(self, v: T):
|
|
82
|
+
self._value = v
|
|
83
|
+
self.notify_listeners()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class CommandNotifier(ValueNotifier[T]):
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
command: Callable, *,
|
|
91
|
+
on_init: Callable = None,
|
|
92
|
+
on_finish: Callable = None,
|
|
93
|
+
on_error: Callable[[Any | None], None] = None,
|
|
94
|
+
value: T = None,
|
|
95
|
+
) -> None:
|
|
96
|
+
super().__init__(value)
|
|
97
|
+
|
|
98
|
+
self.command: Callable = command
|
|
99
|
+
self.on_init: Callable = on_init
|
|
100
|
+
self.on_finish: Callable = on_finish
|
|
101
|
+
self.on_error: Callable[[Any | None], None] = on_error
|
|
102
|
+
self._value: Any = None
|
|
103
|
+
self.running: bool = False
|
|
104
|
+
|
|
105
|
+
def action_error(self, value: Any | None = None) -> None:
|
|
106
|
+
if self.on_error is not None:
|
|
107
|
+
self.on_error(value)
|
|
108
|
+
|
|
109
|
+
def action_init(self):
|
|
110
|
+
self.running = True
|
|
111
|
+
self.notify_listeners()
|
|
112
|
+
if self.on_init is not None:
|
|
113
|
+
self.on_init()
|
|
114
|
+
|
|
115
|
+
def action_finish(self):
|
|
116
|
+
if self.on_finish is not None:
|
|
117
|
+
self.on_finish()
|
|
118
|
+
|
|
119
|
+
def execute(self, *args, **kwargs) -> None:
|
|
120
|
+
try:
|
|
121
|
+
self.action_init()
|
|
122
|
+
self._value = self.command(*args, **kwargs)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
self.action_error(e)
|
|
125
|
+
else:
|
|
126
|
+
self.action_finish()
|
|
127
|
+
finally:
|
|
128
|
+
self.running = False
|
|
129
|
+
self.notify_listeners()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ============================================================
|
|
133
|
+
# CONTEXT
|
|
134
|
+
# ============================================================
|
|
135
|
+
class BuildContext(object):
|
|
136
|
+
|
|
137
|
+
def __init__(
|
|
138
|
+
self, app: BaseMaterialApp, *,
|
|
139
|
+
parent_widget: tk.Widget,
|
|
140
|
+
parent_context: Optional["BuildContext"] = None,
|
|
141
|
+
element: Optional[Element] = None
|
|
142
|
+
) -> None:
|
|
143
|
+
self.app: BaseMaterialApp = app
|
|
144
|
+
self.parent_tk_widget: tk.Widget = parent_widget
|
|
145
|
+
self.parent_context: Optional[BuildContext] = parent_context
|
|
146
|
+
self.element: Optional[Element] = element
|
|
147
|
+
self.name: str = None
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def navigator(self) -> Navigator:
|
|
151
|
+
return self.app.get_navigator()
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def theme(self) -> Any:
|
|
155
|
+
return self.app.theme
|
|
156
|
+
|
|
157
|
+
def create_child_context(
|
|
158
|
+
self,
|
|
159
|
+
parent_widget: tk.Widget,
|
|
160
|
+
element: Optional[Element] = None
|
|
161
|
+
) -> BuildContext:
|
|
162
|
+
return BuildContext(
|
|
163
|
+
app=self.app,
|
|
164
|
+
parent_widget=parent_widget,
|
|
165
|
+
parent_context=self,
|
|
166
|
+
element=element
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def read(self, provider_type: type) -> T:
|
|
170
|
+
return self.app.read_state(provider_type)
|
|
171
|
+
|
|
172
|
+
def watch(self, provider_type: type[T]) -> T:
|
|
173
|
+
if not isinstance(self.element, StatefulElement):
|
|
174
|
+
raise RuntimeError("watch() só pode ser usado dentro de StatefulWidget")
|
|
175
|
+
|
|
176
|
+
provider: T = self.read(provider_type)
|
|
177
|
+
if self.element is not None and isinstance(provider, ChangeNotifier):
|
|
178
|
+
self.element.watch_provider(provider)
|
|
179
|
+
return provider
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ============================================================
|
|
183
|
+
# ELEMENT E WIDGET
|
|
184
|
+
# ============================================================
|
|
185
|
+
class Element[WIDGET_TK]:
|
|
186
|
+
|
|
187
|
+
def __init__(self, widget: FlutterWidget) -> None:
|
|
188
|
+
|
|
189
|
+
self.widget_flutter: FlutterWidget = widget
|
|
190
|
+
self.render_object = RenderObject()
|
|
191
|
+
self._watched_providers: list[ChangeNotifier] = []
|
|
192
|
+
|
|
193
|
+
self.widget_tk: Union[WIDGET_TK, tk.Widget, None] = None
|
|
194
|
+
self._parent_context: BuildContext | None = None
|
|
195
|
+
self._child_element: Element | None = None
|
|
196
|
+
|
|
197
|
+
if widget.key is not None:
|
|
198
|
+
widget.key.element = self
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def layout_data(self) -> LayoutData:
|
|
202
|
+
return self.render_object.layout_data
|
|
203
|
+
|
|
204
|
+
def get_widget_flutter(self) -> FlutterWidget:
|
|
205
|
+
return self.widget_flutter
|
|
206
|
+
|
|
207
|
+
def get_widget_tk(self) -> Union[WIDGET_TK, tk.Widget, None]:
|
|
208
|
+
return self.widget_tk
|
|
209
|
+
|
|
210
|
+
def reattach(self, context: BuildContext) -> None:
|
|
211
|
+
old_parent = self._parent_context.parent_tk_widget if self._parent_context is not None else None
|
|
212
|
+
new_parent = context.parent_tk_widget
|
|
213
|
+
|
|
214
|
+
self._parent_context = context
|
|
215
|
+
self.widget_flutter.context = context
|
|
216
|
+
|
|
217
|
+
if old_parent is not None and old_parent != new_parent:
|
|
218
|
+
self.remount(context)
|
|
219
|
+
|
|
220
|
+
def remount(self, context: BuildContext) -> None:
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
def get_context(self) -> BuildContext | None:
|
|
224
|
+
return self.get_widget_flutter().context
|
|
225
|
+
|
|
226
|
+
def watch_provider(self, provider: ChangeNotifier) -> None:
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
def mount(self, context: BuildContext) -> Union[WIDGET_TK, tk.Widget]:
|
|
230
|
+
self.widget_flutter.context = context
|
|
231
|
+
|
|
232
|
+
if not context.parent_tk_widget.winfo_exists():
|
|
233
|
+
print(f'{__class__.__name__} [ABORTANDO MONTAGEM] .. parent_widget não existe!!!')
|
|
234
|
+
return None # pai já destruído, aborta silenciosamente em vez de TclError
|
|
235
|
+
|
|
236
|
+
return self.get_widget_tk()
|
|
237
|
+
|
|
238
|
+
def update(self, new_widget: FlutterWidget) -> None:
|
|
239
|
+
old_context = self.widget_flutter.context
|
|
240
|
+
new_widget.context = old_context
|
|
241
|
+
self.widget_flutter = new_widget
|
|
242
|
+
|
|
243
|
+
def update_widget(self, widget: FlutterWidget) -> None:
|
|
244
|
+
widget.context = self.widget_flutter.context
|
|
245
|
+
self.widget_flutter = widget
|
|
246
|
+
|
|
247
|
+
def unmount(self) -> None:
|
|
248
|
+
if self.widget_tk is not None:
|
|
249
|
+
try:
|
|
250
|
+
widget_exists(self.get_widget_tk(), destroy=True)
|
|
251
|
+
except Exception:
|
|
252
|
+
pass
|
|
253
|
+
self.widget_tk = None
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class FlutterWidget(ABC):
|
|
259
|
+
|
|
260
|
+
def __init__(self, key: GlobalKey | None = None) -> None:
|
|
261
|
+
self.key: GlobalKey | None = key
|
|
262
|
+
self.context: BuildContext = None
|
|
263
|
+
|
|
264
|
+
@abstractmethod
|
|
265
|
+
def create_element(self) -> Element:
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def inflate_widget(widget: FlutterWidget, context: BuildContext | None = None) -> Element:
|
|
270
|
+
|
|
271
|
+
if widget.key is not None:
|
|
272
|
+
old_element = widget.key.element
|
|
273
|
+
|
|
274
|
+
if old_element is not None:
|
|
275
|
+
if context is not None:
|
|
276
|
+
old_element.reattach(context)
|
|
277
|
+
#old_element.update_widget(widget)
|
|
278
|
+
old_element.update(widget)
|
|
279
|
+
return old_element
|
|
280
|
+
|
|
281
|
+
return widget.create_element()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
# ============================================================
|
|
285
|
+
# STATELESS
|
|
286
|
+
# ============================================================
|
|
287
|
+
class StatelessWidget(FlutterWidget, metaclass=ABCMeta):
|
|
288
|
+
|
|
289
|
+
def __init__(self, key: GlobalKey | None = None) -> None:
|
|
290
|
+
super().__init__(key)
|
|
291
|
+
|
|
292
|
+
@abstractmethod
|
|
293
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
294
|
+
pass
|
|
295
|
+
|
|
296
|
+
def create_element(self) -> Element:
|
|
297
|
+
return StatelessElement(self)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class StatelessElement(Element):
|
|
301
|
+
|
|
302
|
+
def __init__(self, widget: StatelessWidget) -> None:
|
|
303
|
+
super().__init__(widget)
|
|
304
|
+
self.child_element: Element | None = None
|
|
305
|
+
|
|
306
|
+
def get_widget_flutter(self) -> StatelessWidget:
|
|
307
|
+
return self.widget_flutter
|
|
308
|
+
|
|
309
|
+
def reattach(self, context: BuildContext) -> None:
|
|
310
|
+
super().reattach(context)
|
|
311
|
+
|
|
312
|
+
if self.child_element is not None:
|
|
313
|
+
|
|
314
|
+
child_context = BuildContext(
|
|
315
|
+
app=context.app,
|
|
316
|
+
parent_widget=context.parent_tk_widget,
|
|
317
|
+
parent_context=context,
|
|
318
|
+
element=self.child_element
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
self.child_element.reattach(child_context)
|
|
322
|
+
|
|
323
|
+
def mount(self, context: BuildContext) -> tk.Widget:
|
|
324
|
+
super().mount(context)
|
|
325
|
+
self._parent_context = context
|
|
326
|
+
child = self.get_widget_flutter().build(context)
|
|
327
|
+
|
|
328
|
+
self.child_element = inflate_widget(child, context)
|
|
329
|
+
self.widget_tk = self.child_element.mount(context)
|
|
330
|
+
return self.widget_tk
|
|
331
|
+
|
|
332
|
+
def remount(self, context: BuildContext) -> None:
|
|
333
|
+
if self.child_element is not None:
|
|
334
|
+
self.child_element.unmount()
|
|
335
|
+
|
|
336
|
+
self.mount(context)
|
|
337
|
+
|
|
338
|
+
def update(self, new_widget: FlutterWidget) -> None:
|
|
339
|
+
super().update(new_widget)
|
|
340
|
+
self.widget_flutter = cast(StatelessWidget, new_widget)
|
|
341
|
+
if self.child_element is None:
|
|
342
|
+
return
|
|
343
|
+
if self.get_context() is None:
|
|
344
|
+
return
|
|
345
|
+
|
|
346
|
+
new_child = self.get_widget_flutter().build(self.get_context())
|
|
347
|
+
|
|
348
|
+
if type(new_child) is type(self.child_element.widget_flutter):
|
|
349
|
+
self.child_element.update(new_child)
|
|
350
|
+
else:
|
|
351
|
+
#parent = self.get_context().parent_tk_widget
|
|
352
|
+
self.child_element.unmount()
|
|
353
|
+
self.child_element = inflate_widget(new_child)
|
|
354
|
+
self.widget_tk = self.child_element.mount(self.get_context())
|
|
355
|
+
|
|
356
|
+
def update_widget(self, widget: FlutterWidget) -> None:
|
|
357
|
+
super().update_widget(widget)
|
|
358
|
+
|
|
359
|
+
def unmount(self) -> None:
|
|
360
|
+
if self.child_element is not None:
|
|
361
|
+
self.child_element.unmount()
|
|
362
|
+
self.child_element = None
|
|
363
|
+
self.widget_tk = None
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class InheritedWidget(StatelessWidget):
|
|
367
|
+
|
|
368
|
+
def __init__(self, child: FlutterWidget) -> None:
|
|
369
|
+
super().__init__()
|
|
370
|
+
self.child = child
|
|
371
|
+
|
|
372
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
373
|
+
return self.child
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
# ============================================================
|
|
377
|
+
# STATE
|
|
378
|
+
# ============================================================
|
|
379
|
+
|
|
380
|
+
class State(ABC):
|
|
381
|
+
|
|
382
|
+
def __init__(self) -> None:
|
|
383
|
+
self.context: Optional[BuildContext] = None
|
|
384
|
+
self._element: Optional[StatefulElement] = None
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def widget(self) -> StatefulWidget:
|
|
388
|
+
if self._element is None:
|
|
389
|
+
raise RuntimeError()
|
|
390
|
+
return self._element.get_widget_flutter()
|
|
391
|
+
|
|
392
|
+
def init_state(self) -> None:
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
def dispose(self) -> None:
|
|
396
|
+
pass
|
|
397
|
+
|
|
398
|
+
def did_update(self) -> None:
|
|
399
|
+
pass
|
|
400
|
+
|
|
401
|
+
def set_state(self, callback: Callable[[], None]) -> None:
|
|
402
|
+
callback()
|
|
403
|
+
if self._element:
|
|
404
|
+
self._element.rebuild()
|
|
405
|
+
|
|
406
|
+
@abstractmethod
|
|
407
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class ConsumerState[T](State):
|
|
412
|
+
def __init__(self) -> None:
|
|
413
|
+
super().__init__()
|
|
414
|
+
|
|
415
|
+
@property
|
|
416
|
+
def widget(self) -> ValueListenableBuilder:
|
|
417
|
+
return cast(ValueListenableBuilder, super().widget)
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def provider(self) -> T:
|
|
421
|
+
if self.context is None:
|
|
422
|
+
raise RuntimeError()
|
|
423
|
+
|
|
424
|
+
return self.context.read(
|
|
425
|
+
self.provider_type()
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
@abstractmethod
|
|
429
|
+
def provider_type(self) -> type[T]:
|
|
430
|
+
pass
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
# ============================================================
|
|
434
|
+
# STATEFUL
|
|
435
|
+
# ============================================================
|
|
436
|
+
class StatefulWidget[V](FlutterWidget, metaclass=ABCMeta):
|
|
437
|
+
|
|
438
|
+
def __init__(self, key: str | None = None) -> None:
|
|
439
|
+
super().__init__(key)
|
|
440
|
+
|
|
441
|
+
@abstractmethod
|
|
442
|
+
def create_state(self) -> State:
|
|
443
|
+
pass
|
|
444
|
+
|
|
445
|
+
def create_element(self) -> Element:
|
|
446
|
+
return StatefulElement(self)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
class StatefulElement(Element):
|
|
450
|
+
|
|
451
|
+
def __init__(self, widget: StatefulWidget) -> None:
|
|
452
|
+
super().__init__(widget)
|
|
453
|
+
self.state: State = widget.create_state()
|
|
454
|
+
self.state._element = self
|
|
455
|
+
self.child_element: Element | None = None
|
|
456
|
+
self._initialized = False
|
|
457
|
+
|
|
458
|
+
def get_widget_flutter(self) -> StatefulWidget:
|
|
459
|
+
return self.widget_flutter
|
|
460
|
+
|
|
461
|
+
def reattach(self, context: BuildContext) -> None:
|
|
462
|
+
super().reattach(context)
|
|
463
|
+
|
|
464
|
+
state_context = BuildContext(
|
|
465
|
+
app=context.app,
|
|
466
|
+
parent_widget=context.parent_tk_widget,
|
|
467
|
+
parent_context=context,
|
|
468
|
+
element=self
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
self.state.context = state_context
|
|
472
|
+
self.widget_flutter.context = state_context
|
|
473
|
+
|
|
474
|
+
if self.child_element is not None:
|
|
475
|
+
self.child_element.reattach(state_context)
|
|
476
|
+
|
|
477
|
+
def mount(self, context: BuildContext) -> tk.Widget:
|
|
478
|
+
super().mount(context)
|
|
479
|
+
|
|
480
|
+
self._parent_context = context
|
|
481
|
+
|
|
482
|
+
self.state.context = BuildContext(
|
|
483
|
+
app=context.app,
|
|
484
|
+
parent_widget=context.parent_tk_widget,
|
|
485
|
+
parent_context=context,
|
|
486
|
+
element=self
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
self.widget_flutter.context = self.state.context
|
|
490
|
+
|
|
491
|
+
if not self._initialized:
|
|
492
|
+
self.state.init_state()
|
|
493
|
+
self._initialized = True
|
|
494
|
+
|
|
495
|
+
child = self.state.build(self.state.context)
|
|
496
|
+
|
|
497
|
+
if self.child_element is None:
|
|
498
|
+
self.child_element = inflate_widget(child, self.state.context)
|
|
499
|
+
self.widget_tk = self.child_element.mount(self.state.context)
|
|
500
|
+
return self.widget_tk
|
|
501
|
+
|
|
502
|
+
def remount(self, context: BuildContext) -> None:
|
|
503
|
+
old_child = self.child_element
|
|
504
|
+
if old_child is not None:
|
|
505
|
+
old_child.unmount()
|
|
506
|
+
|
|
507
|
+
self.child_element = None
|
|
508
|
+
self.widget_tk = None
|
|
509
|
+
self.mount(context)
|
|
510
|
+
|
|
511
|
+
def rebuild(self) -> None:
|
|
512
|
+
# _parent_context é o contexto recebido em mount() — seu parent_tk_widget
|
|
513
|
+
# é o frame pai original, que continua vivo entre rebuilds.
|
|
514
|
+
# state.context pode apontar para um frame filho destruído pelo
|
|
515
|
+
# unmount() anterior do ColumnElement, por isso reconstrói o contexto aqui.
|
|
516
|
+
if self._parent_context is None:
|
|
517
|
+
return
|
|
518
|
+
|
|
519
|
+
# Garante que parent_tk_widget é sempre o frame pai vivo, não o filho destruído.
|
|
520
|
+
self.state.context = BuildContext(
|
|
521
|
+
app=self._parent_context.app,
|
|
522
|
+
parent_widget=self._parent_context.parent_tk_widget,
|
|
523
|
+
parent_context=self._parent_context,
|
|
524
|
+
element=self,
|
|
525
|
+
)
|
|
526
|
+
self.widget_flutter.context = self.state.context
|
|
527
|
+
|
|
528
|
+
context = self.state.context
|
|
529
|
+
new_child = self.state.build(context)
|
|
530
|
+
|
|
531
|
+
if self.child_element is not None and type(new_child) is type(self.child_element.widget_flutter):
|
|
532
|
+
self.child_element.update(new_child)
|
|
533
|
+
return
|
|
534
|
+
|
|
535
|
+
if self.child_element is not None:
|
|
536
|
+
self.child_element.unmount()
|
|
537
|
+
|
|
538
|
+
self.child_element = inflate_widget(new_child)
|
|
539
|
+
self.widget_tk = self.child_element.mount(context)
|
|
540
|
+
|
|
541
|
+
def unmount(self) -> None:
|
|
542
|
+
|
|
543
|
+
if self.child_element is not None:
|
|
544
|
+
self.child_element.unmount()
|
|
545
|
+
self.child_element = None
|
|
546
|
+
|
|
547
|
+
try:
|
|
548
|
+
self.state.dispose()
|
|
549
|
+
finally:
|
|
550
|
+
self.state.context = None
|
|
551
|
+
self.state._element = None
|
|
552
|
+
super().unmount()
|
|
553
|
+
|
|
554
|
+
def update(self, new_widget: FlutterWidget) -> None:
|
|
555
|
+
super().update(new_widget)
|
|
556
|
+
|
|
557
|
+
def update_widget(self, widget: FlutterWidget) -> None:
|
|
558
|
+
super().update_widget(widget)
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
class ValueListenableBuilder[T](StatefulWidget):
|
|
562
|
+
|
|
563
|
+
def __init__(
|
|
564
|
+
self,
|
|
565
|
+
notifier: ValueNotifier[T],
|
|
566
|
+
builder: Callable[[BuildContext, T], FlutterWidget]
|
|
567
|
+
) -> None:
|
|
568
|
+
super().__init__()
|
|
569
|
+
self.notifier = notifier
|
|
570
|
+
self.builder = builder
|
|
571
|
+
|
|
572
|
+
def create_state(self) -> State:
|
|
573
|
+
return (_ValueListenableBuilderState())
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
class _ValueListenableBuilderState(State):
|
|
577
|
+
|
|
578
|
+
def __init__(self) -> None:
|
|
579
|
+
super().__init__()
|
|
580
|
+
|
|
581
|
+
def init_state(self) -> None:
|
|
582
|
+
widget: ValueListenableBuilder = cast(ValueListenableBuilder, self._element.widget_flutter)
|
|
583
|
+
widget.notifier.add_listener(self._listener)
|
|
584
|
+
|
|
585
|
+
def dispose(self) -> None:
|
|
586
|
+
widget: ValueListenableBuilder = cast(ValueListenableBuilder, self._element.widget_flutter)
|
|
587
|
+
widget.notifier.remove_listener(self._listener)
|
|
588
|
+
|
|
589
|
+
def _listener(self) -> None:
|
|
590
|
+
self.set_state(lambda: None)
|
|
591
|
+
|
|
592
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
593
|
+
widget: ValueListenableBuilder = cast(ValueListenableBuilder, self._element.widget_flutter)
|
|
594
|
+
return widget.builder(context, widget.notifier.value)
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
# ============================================================
|
|
598
|
+
# PROVIDER
|
|
599
|
+
# ============================================================
|
|
600
|
+
class MapState[K, T]:
|
|
601
|
+
|
|
602
|
+
_instance: "MapState | None" = None
|
|
603
|
+
|
|
604
|
+
def __new__(cls, *args, **kwargs) -> "MapState":
|
|
605
|
+
if cls._instance is None:
|
|
606
|
+
cls._instance = super().__new__(cls)
|
|
607
|
+
return cls._instance
|
|
608
|
+
|
|
609
|
+
def __init__(self) -> None:
|
|
610
|
+
if not hasattr(self, "values"):
|
|
611
|
+
self.values: dict = dict()
|
|
612
|
+
|
|
613
|
+
def __setitem__(self, key, value):
|
|
614
|
+
self.values[key] = value
|
|
615
|
+
|
|
616
|
+
def __getitem__(self, key):
|
|
617
|
+
return self.values[key]
|
|
618
|
+
|
|
619
|
+
def contains_key(self, key) -> bool:
|
|
620
|
+
for k in self.values.keys():
|
|
621
|
+
if k == key:
|
|
622
|
+
return True
|
|
623
|
+
return False
|
|
624
|
+
|
|
625
|
+
def add(self, value: T):
|
|
626
|
+
_type_value = type(value)
|
|
627
|
+
if not self.contains_key(_type_value):
|
|
628
|
+
self.values[_type_value] = value
|
|
629
|
+
|
|
630
|
+
def read[T](self, key: type[T]) -> T:
|
|
631
|
+
return self.values[key]
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
class Provider(object):
|
|
635
|
+
|
|
636
|
+
_states: MapState = MapState()
|
|
637
|
+
_instance: "Provider | None" = None
|
|
638
|
+
|
|
639
|
+
def __new__(cls) -> "Provider":
|
|
640
|
+
if cls._instance is None:
|
|
641
|
+
cls._instance = super().__new__(cls)
|
|
642
|
+
return cls._instance
|
|
643
|
+
|
|
644
|
+
def __init__(self) -> None:
|
|
645
|
+
pass
|
|
646
|
+
|
|
647
|
+
@classmethod
|
|
648
|
+
def add_state(cls, notifier: T) -> None:
|
|
649
|
+
print(f'DEBUG => Estado adicionado ... {notifier}')
|
|
650
|
+
cls._states.add(notifier)
|
|
651
|
+
|
|
652
|
+
@classmethod
|
|
653
|
+
def read(cls, type_notifier: type[T]) -> T:
|
|
654
|
+
return cls._states.read(type_notifier)
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
class Consumer[V](StatefulWidget):
|
|
658
|
+
|
|
659
|
+
def __init__(
|
|
660
|
+
self,
|
|
661
|
+
provider_type: type[V],
|
|
662
|
+
builder: Callable[[BuildContext, Any], FlutterWidget]
|
|
663
|
+
) -> None:
|
|
664
|
+
super().__init__()
|
|
665
|
+
self.provider_type: type[V] = provider_type
|
|
666
|
+
self.builder: Callable[[BuildContext, Any], FlutterWidget] = builder
|
|
667
|
+
|
|
668
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
669
|
+
provider = context.read(self.provider_type)
|
|
670
|
+
return self.builder(context, provider)
|
|
671
|
+
|
|
672
|
+
def create_state(self) -> State:
|
|
673
|
+
return _ConsumerState()
|
|
674
|
+
|
|
675
|
+
def create_element(self) -> Element:
|
|
676
|
+
return ConsumerElement(self)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
class _ConsumerState(State):
|
|
680
|
+
|
|
681
|
+
def __init__(self) -> None:
|
|
682
|
+
super().__init__()
|
|
683
|
+
|
|
684
|
+
def build(self, context: BuildContext) -> FlutterWidget:
|
|
685
|
+
widget: Consumer = cast(Consumer, self.widget)
|
|
686
|
+
provider = context.watch(widget.provider_type)
|
|
687
|
+
return widget.builder(context, provider)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
class ConsumerElement(StatefulElement):
|
|
691
|
+
|
|
692
|
+
def __init__(self, widget: Consumer) -> None:
|
|
693
|
+
super().__init__(widget)
|
|
694
|
+
self._providers: list[ChangeNotifier] = []
|
|
695
|
+
|
|
696
|
+
def watch_provider(self, provider: ChangeNotifier) -> None:
|
|
697
|
+
|
|
698
|
+
if provider in self._providers:
|
|
699
|
+
return
|
|
700
|
+
|
|
701
|
+
provider.add_listener(self._listener)
|
|
702
|
+
self._providers.append(provider)
|
|
703
|
+
|
|
704
|
+
def _listener_fixed(self) -> None:
|
|
705
|
+
try:
|
|
706
|
+
self.rebuild()
|
|
707
|
+
except Exception:
|
|
708
|
+
import traceback
|
|
709
|
+
traceback.print_exc()
|
|
710
|
+
|
|
711
|
+
def _listener(self) -> None:
|
|
712
|
+
self.rebuild()
|
|
713
|
+
#self._listener_fixed()
|
|
714
|
+
|
|
715
|
+
def unmount(self) -> None:
|
|
716
|
+
for provider in self._providers:
|
|
717
|
+
provider.remove_listener(self._listener)
|
|
718
|
+
self._providers.clear()
|
|
719
|
+
super().unmount()
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
# ============================================================
|
|
723
|
+
# APP
|
|
724
|
+
# ============================================================
|
|
725
|
+
|
|
726
|
+
class BaseMaterialApp(ABC):
|
|
727
|
+
|
|
728
|
+
def __init__(
|
|
729
|
+
self,
|
|
730
|
+
home: FlutterWidget,
|
|
731
|
+
title: str = "TkFlutter",
|
|
732
|
+
theme: Any = None
|
|
733
|
+
) -> None:
|
|
734
|
+
super().__init__()
|
|
735
|
+
self.home: FlutterWidget = home
|
|
736
|
+
self.title: str = title
|
|
737
|
+
self.theme: Any = theme
|
|
738
|
+
self.root: tk.Tk = None
|
|
739
|
+
self.provider: Provider = Provider()
|
|
740
|
+
|
|
741
|
+
@abstractmethod
|
|
742
|
+
def get_navigator(self) -> Navigator:
|
|
743
|
+
pass
|
|
744
|
+
|
|
745
|
+
def register_notifier[T](self, notifier: T) -> None:
|
|
746
|
+
self.provider.add_state(notifier)
|
|
747
|
+
|
|
748
|
+
def read_state[T](self, type_notifier: type[T]) -> T:
|
|
749
|
+
return self.provider.read(type_notifier)
|
|
750
|
+
|
|
751
|
+
def run(self) -> None:
|
|
752
|
+
pass
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
class Navigator(object):
|
|
756
|
+
|
|
757
|
+
def __init__(self, app: BaseMaterialApp) -> None:
|
|
758
|
+
super().__init__()
|
|
759
|
+
self.stack: list[Element] = []
|
|
760
|
+
self.app: BaseMaterialApp = app
|
|
761
|
+
|
|
762
|
+
def push(self, page: FlutterWidget) -> None:
|
|
763
|
+
pass
|
|
764
|
+
|
|
765
|
+
def pop(self) -> None:
|
|
766
|
+
pass
|
|
767
|
+
|
|
768
|
+
def replace(self, page: FlutterWidget) -> None:
|
|
769
|
+
pass
|
|
770
|
+
|
|
771
|
+
def render(self) -> None:
|
|
772
|
+
pass
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
# ============================================================
|
|
776
|
+
# LAYOUT DATA
|
|
777
|
+
# ============================================================
|
|
778
|
+
class LayoutData(object):
|
|
779
|
+
|
|
780
|
+
def __init__(
|
|
781
|
+
self,
|
|
782
|
+
flex: int = 0,
|
|
783
|
+
expand: bool = False,
|
|
784
|
+
fill_x: bool = False,
|
|
785
|
+
fill_y: bool = False
|
|
786
|
+
) -> None:
|
|
787
|
+
super().__init__()
|
|
788
|
+
self.flex: int = flex
|
|
789
|
+
self.expand: bool = expand
|
|
790
|
+
self.fill_x: bool = fill_x
|
|
791
|
+
self.fill_y: bool = fill_y
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
# ============================================================
|
|
795
|
+
# RENDER OBJECT
|
|
796
|
+
# ============================================================
|
|
797
|
+
|
|
798
|
+
class RenderObject(object):
|
|
799
|
+
|
|
800
|
+
def __init__(self) -> None:
|
|
801
|
+
super().__init__()
|
|
802
|
+
self.layout_data: LayoutData = (LayoutData())
|
|
803
|
+
self.parent: (RenderObject | None) = None
|
|
804
|
+
self.children: list[RenderObject] = list()
|
|
805
|
+
self.tk_widget: (tk.Widget | None) = None
|
|
806
|
+
|
|
807
|
+
def add_child(self, child: "RenderObject") -> None:
|
|
808
|
+
child.parent = self
|
|
809
|
+
self.children.append(child)
|
|
810
|
+
|
|
811
|
+
def remove_child(self, child: RenderObject) -> None:
|
|
812
|
+
if child in self.children:
|
|
813
|
+
self.children.remove(child)
|
|
814
|
+
child.parent = None
|
|
815
|
+
|
|
816
|
+
def attach_widget(self, widget: tk.Widget) -> None:
|
|
817
|
+
self.tk_widget = widget
|
|
818
|
+
|
|
819
|
+
def apply_layout(self) -> None:
|
|
820
|
+
pass
|