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.
@@ -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