py10x-universe 0.1.3__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.
Files changed (214) hide show
  1. core_10x/__init__.py +42 -0
  2. core_10x/backbone/__init__.py +0 -0
  3. core_10x/backbone/backbone_store.py +59 -0
  4. core_10x/backbone/backbone_traitable.py +30 -0
  5. core_10x/backbone/backbone_user.py +66 -0
  6. core_10x/backbone/bound_data_domain.py +49 -0
  7. core_10x/backbone/namespace.py +101 -0
  8. core_10x/backbone/vault.py +38 -0
  9. core_10x/code_samples/__init__.py +0 -0
  10. core_10x/code_samples/_package_manifest.py +3 -0
  11. core_10x/code_samples/directories.py +181 -0
  12. core_10x/code_samples/person.py +76 -0
  13. core_10x/concrete_traits.py +356 -0
  14. core_10x/conftest.py +12 -0
  15. core_10x/curve.py +321 -0
  16. core_10x/data_domain.py +48 -0
  17. core_10x/data_domain_binder.py +45 -0
  18. core_10x/directory.py +250 -0
  19. core_10x/entity.py +8 -0
  20. core_10x/entity_filter.py +5 -0
  21. core_10x/environment_variables.py +147 -0
  22. core_10x/exec_control.py +84 -0
  23. core_10x/experimental/__init__.py +0 -0
  24. core_10x/experimental/data_protocol_ex.py +34 -0
  25. core_10x/global_cache.py +121 -0
  26. core_10x/manual_tests/__init__.py +0 -0
  27. core_10x/manual_tests/calendar_test.py +35 -0
  28. core_10x/manual_tests/ctor_update_bug.py +58 -0
  29. core_10x/manual_tests/debug_graph_on.py +17 -0
  30. core_10x/manual_tests/debug_graphoff_inside_graph_on.py +28 -0
  31. core_10x/manual_tests/enum_bits_test.py +17 -0
  32. core_10x/manual_tests/env_vars_trivial_test.py +12 -0
  33. core_10x/manual_tests/existing_traitable.py +33 -0
  34. core_10x/manual_tests/k10x_test1.py +13 -0
  35. core_10x/manual_tests/named_constant_test.py +121 -0
  36. core_10x/manual_tests/nucleus_trivial_test.py +42 -0
  37. core_10x/manual_tests/polars_test.py +14 -0
  38. core_10x/manual_tests/py_class_test.py +4 -0
  39. core_10x/manual_tests/rc_test.py +42 -0
  40. core_10x/manual_tests/rdate_test.py +12 -0
  41. core_10x/manual_tests/reference_serialization_bug.py +19 -0
  42. core_10x/manual_tests/resource_trivial_test.py +10 -0
  43. core_10x/manual_tests/store_uri_test.py +6 -0
  44. core_10x/manual_tests/trait_definition_test.py +19 -0
  45. core_10x/manual_tests/trait_filter_test.py +15 -0
  46. core_10x/manual_tests/trait_flag_modification_test.py +42 -0
  47. core_10x/manual_tests/trait_modification_bug.py +26 -0
  48. core_10x/manual_tests/traitable_as_of_test.py +82 -0
  49. core_10x/manual_tests/traitable_heir_test.py +39 -0
  50. core_10x/manual_tests/traitable_history_test.py +41 -0
  51. core_10x/manual_tests/traitable_serialization_test.py +54 -0
  52. core_10x/manual_tests/traitable_trivial_test.py +71 -0
  53. core_10x/manual_tests/trivial_graph_test.py +16 -0
  54. core_10x/manual_tests/ts_class_association_test.py +64 -0
  55. core_10x/manual_tests/ts_trivial_test.py +35 -0
  56. core_10x/named_constant.py +425 -0
  57. core_10x/nucleus.py +81 -0
  58. core_10x/package_manifest.py +85 -0
  59. core_10x/package_refactoring.py +153 -0
  60. core_10x/py_class.py +431 -0
  61. core_10x/rc.py +155 -0
  62. core_10x/rdate.py +339 -0
  63. core_10x/resource.py +189 -0
  64. core_10x/roman_number.py +67 -0
  65. core_10x/testlib/__init__.py +0 -0
  66. core_10x/testlib/test_store.py +240 -0
  67. core_10x/testlib/traitable_history_tests.py +787 -0
  68. core_10x/testlib/ts_tests.py +280 -0
  69. core_10x/trait.py +377 -0
  70. core_10x/trait_definition.py +176 -0
  71. core_10x/trait_filter.py +205 -0
  72. core_10x/trait_method_error.py +36 -0
  73. core_10x/traitable.py +1082 -0
  74. core_10x/traitable_cli.py +153 -0
  75. core_10x/traitable_heir.py +33 -0
  76. core_10x/traitable_id.py +31 -0
  77. core_10x/ts_store.py +172 -0
  78. core_10x/ts_store_type.py +26 -0
  79. core_10x/ts_union.py +147 -0
  80. core_10x/ui_hint.py +153 -0
  81. core_10x/unit_tests/test_concrete_traits.py +156 -0
  82. core_10x/unit_tests/test_converters.py +51 -0
  83. core_10x/unit_tests/test_curve.py +157 -0
  84. core_10x/unit_tests/test_directory.py +54 -0
  85. core_10x/unit_tests/test_documentation.py +172 -0
  86. core_10x/unit_tests/test_environment_variables.py +15 -0
  87. core_10x/unit_tests/test_filters.py +239 -0
  88. core_10x/unit_tests/test_graph.py +348 -0
  89. core_10x/unit_tests/test_named_constant.py +98 -0
  90. core_10x/unit_tests/test_rc.py +11 -0
  91. core_10x/unit_tests/test_rdate.py +484 -0
  92. core_10x/unit_tests/test_trait_method_error.py +80 -0
  93. core_10x/unit_tests/test_trait_modification.py +19 -0
  94. core_10x/unit_tests/test_traitable.py +959 -0
  95. core_10x/unit_tests/test_traitable_history.py +1 -0
  96. core_10x/unit_tests/test_ts_store.py +1 -0
  97. core_10x/unit_tests/test_ts_union.py +369 -0
  98. core_10x/unit_tests/test_ui_nodes.py +81 -0
  99. core_10x/unit_tests/test_xxcalendar.py +471 -0
  100. core_10x/vault/__init__.py +0 -0
  101. core_10x/vault/sec_keys.py +133 -0
  102. core_10x/vault/security_keys_old.py +168 -0
  103. core_10x/vault/vault.py +56 -0
  104. core_10x/vault/vault_traitable.py +56 -0
  105. core_10x/vault/vault_user.py +70 -0
  106. core_10x/xdate_time.py +136 -0
  107. core_10x/xnone.py +71 -0
  108. core_10x/xxcalendar.py +228 -0
  109. infra_10x/__init__.py +0 -0
  110. infra_10x/manual_tests/__init__.py +0 -0
  111. infra_10x/manual_tests/test_misc.py +16 -0
  112. infra_10x/manual_tests/test_prepare_filter_and_pipeline.py +25 -0
  113. infra_10x/mongodb_admin.py +111 -0
  114. infra_10x/mongodb_store.py +346 -0
  115. infra_10x/mongodb_utils.py +129 -0
  116. infra_10x/unit_tests/conftest.py +13 -0
  117. infra_10x/unit_tests/test_mongo_db.py +36 -0
  118. infra_10x/unit_tests/test_mongo_history.py +1 -0
  119. py10x_universe-0.1.3.dist-info/METADATA +406 -0
  120. py10x_universe-0.1.3.dist-info/RECORD +214 -0
  121. py10x_universe-0.1.3.dist-info/WHEEL +4 -0
  122. py10x_universe-0.1.3.dist-info/licenses/LICENSE +21 -0
  123. ui_10x/__init__.py +0 -0
  124. ui_10x/apps/__init__.py +0 -0
  125. ui_10x/apps/collection_editor_app.py +100 -0
  126. ui_10x/choice.py +212 -0
  127. ui_10x/collection_editor.py +135 -0
  128. ui_10x/concrete_trait_widgets.py +220 -0
  129. ui_10x/conftest.py +8 -0
  130. ui_10x/entity_stocker.py +173 -0
  131. ui_10x/examples/__init__.py +0 -0
  132. ui_10x/examples/_guess_word_data.py +14076 -0
  133. ui_10x/examples/collection_editor.py +17 -0
  134. ui_10x/examples/date_selector.py +14 -0
  135. ui_10x/examples/entity_stocker.py +18 -0
  136. ui_10x/examples/guess_word.py +392 -0
  137. ui_10x/examples/message_box.py +20 -0
  138. ui_10x/examples/multi_choice.py +17 -0
  139. ui_10x/examples/py_data_browser.py +66 -0
  140. ui_10x/examples/radiobox.py +29 -0
  141. ui_10x/examples/single_choice.py +31 -0
  142. ui_10x/examples/style_sheet.py +47 -0
  143. ui_10x/examples/trivial_entity_editor.py +18 -0
  144. ui_10x/platform.py +20 -0
  145. ui_10x/platform_interface.py +517 -0
  146. ui_10x/py_data_browser.py +249 -0
  147. ui_10x/qt6/__init__.py +0 -0
  148. ui_10x/qt6/conftest.py +8 -0
  149. ui_10x/qt6/manual_tests/__init__.py +0 -0
  150. ui_10x/qt6/manual_tests/basic_test.py +35 -0
  151. ui_10x/qt6/platform_implementation.py +275 -0
  152. ui_10x/qt6/utils.py +665 -0
  153. ui_10x/rio/__init__.py +0 -0
  154. ui_10x/rio/apps/examples/examples/__init__.py +22 -0
  155. ui_10x/rio/apps/examples/examples/components/__init__.py +3 -0
  156. ui_10x/rio/apps/examples/examples/components/collection_editor.py +15 -0
  157. ui_10x/rio/apps/examples/examples/pages/collection_editor.py +21 -0
  158. ui_10x/rio/apps/examples/examples/pages/login_page.py +88 -0
  159. ui_10x/rio/apps/examples/examples/pages/style_sheet.py +21 -0
  160. ui_10x/rio/apps/examples/rio.toml +14 -0
  161. ui_10x/rio/component_builder.py +497 -0
  162. ui_10x/rio/components/__init__.py +9 -0
  163. ui_10x/rio/components/group_box.py +31 -0
  164. ui_10x/rio/components/labeled_checkbox.py +18 -0
  165. ui_10x/rio/components/line_edit.py +37 -0
  166. ui_10x/rio/components/radio_button.py +32 -0
  167. ui_10x/rio/components/separator.py +24 -0
  168. ui_10x/rio/components/splitter.py +121 -0
  169. ui_10x/rio/components/tree_view.py +75 -0
  170. ui_10x/rio/conftest.py +35 -0
  171. ui_10x/rio/internals/__init__.py +0 -0
  172. ui_10x/rio/internals/app.py +192 -0
  173. ui_10x/rio/manual_tests/__init__.py +0 -0
  174. ui_10x/rio/manual_tests/basic_test.py +24 -0
  175. ui_10x/rio/manual_tests/splitter.py +27 -0
  176. ui_10x/rio/platform_implementation.py +91 -0
  177. ui_10x/rio/style_sheet.py +53 -0
  178. ui_10x/rio/unit_tests/test_collection_editor.py +68 -0
  179. ui_10x/rio/unit_tests/test_internals.py +630 -0
  180. ui_10x/rio/unit_tests/test_style_sheet.py +37 -0
  181. ui_10x/rio/widgets/__init__.py +46 -0
  182. ui_10x/rio/widgets/application.py +109 -0
  183. ui_10x/rio/widgets/button.py +48 -0
  184. ui_10x/rio/widgets/button_group.py +60 -0
  185. ui_10x/rio/widgets/calendar.py +23 -0
  186. ui_10x/rio/widgets/checkbox.py +24 -0
  187. ui_10x/rio/widgets/dialog.py +137 -0
  188. ui_10x/rio/widgets/group_box.py +27 -0
  189. ui_10x/rio/widgets/layout.py +34 -0
  190. ui_10x/rio/widgets/line_edit.py +37 -0
  191. ui_10x/rio/widgets/list.py +105 -0
  192. ui_10x/rio/widgets/message_box.py +70 -0
  193. ui_10x/rio/widgets/scroll_area.py +31 -0
  194. ui_10x/rio/widgets/spacer.py +6 -0
  195. ui_10x/rio/widgets/splitter.py +45 -0
  196. ui_10x/rio/widgets/text_edit.py +28 -0
  197. ui_10x/rio/widgets/tree.py +89 -0
  198. ui_10x/rio/widgets/unit_tests/test_button.py +101 -0
  199. ui_10x/rio/widgets/unit_tests/test_button_group.py +33 -0
  200. ui_10x/rio/widgets/unit_tests/test_calendar.py +114 -0
  201. ui_10x/rio/widgets/unit_tests/test_checkbox.py +109 -0
  202. ui_10x/rio/widgets/unit_tests/test_group_box.py +158 -0
  203. ui_10x/rio/widgets/unit_tests/test_label.py +43 -0
  204. ui_10x/rio/widgets/unit_tests/test_line_edit.py +140 -0
  205. ui_10x/rio/widgets/unit_tests/test_list.py +146 -0
  206. ui_10x/table_header_view.py +305 -0
  207. ui_10x/table_view.py +174 -0
  208. ui_10x/trait_editor.py +189 -0
  209. ui_10x/trait_widget.py +131 -0
  210. ui_10x/traitable_editor.py +200 -0
  211. ui_10x/traitable_view.py +131 -0
  212. ui_10x/unit_tests/conftest.py +8 -0
  213. ui_10x/unit_tests/test_platform.py +9 -0
  214. ui_10x/utils.py +661 -0
@@ -0,0 +1,70 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ import ui_10x.platform_interface as i
6
+ import ui_10x.rio.components as rio_components
7
+ from ui_10x.rio.component_builder import Widget
8
+
9
+ # noinspection PyCompatibility
10
+ from . import (
11
+ Application,
12
+ Dialog,
13
+ HBoxLayout,
14
+ Label,
15
+ PushButton,
16
+ Style,
17
+ VBoxLayout,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Callable
22
+
23
+
24
+ class Separator(Widget, i.Separator):
25
+ s_component_class = rio_components.Separator
26
+ s_forced_kwargs = {}
27
+
28
+
29
+ class MessageBox(i.MessageBox):
30
+ @classmethod
31
+ def _dialog(cls, parent: Widget, title: str, message: str, icon: Style.StandardPixmap, on_close: Callable[[Any], None]) -> bool | None:
32
+ style = Application.style()
33
+ if icon == Style.StandardPixmap.SP_MESSAGEBOXQUESTION:
34
+ buttons = [
35
+ PushButton(style.standard_icon(Style.StandardPixmap.SP_DIALOGYESBUTTON.value), 'Yes', on_press=lambda: dlg.on_accept()),
36
+ PushButton(style.standard_icon(Style.StandardPixmap.SP_DIALOGNOBUTTON.value), 'No', on_press=lambda: dlg.on_reject()),
37
+ ]
38
+ else:
39
+ buttons = [PushButton(style.standard_icon(Style.StandardPixmap.SP_DIALOGOKBUTTON.value), 'Ok', on_press=lambda: dlg.on_accept())]
40
+ children = (
41
+ Label(title, align_x=0.5, align_y=0, style='heading1'),
42
+ Separator(),
43
+ HBoxLayout(
44
+ PushButton(style.standard_icon(icon.value), '', style='plain-text', is_sensitive=False, align_y=0, align_x=0, grow_x=False),
45
+ Label(message, align_y=0, overflow='wrap', grow_x=True),
46
+ ),
47
+ Separator(),
48
+ HBoxLayout(*buttons),
49
+ )
50
+ dlg = Dialog(
51
+ parent=parent, title=title, children=children, on_accept=lambda: on_close(dlg.accepted), on_reject=lambda: on_close(dlg.accepted)
52
+ )
53
+ dlg.set_layout(VBoxLayout())
54
+ return dlg.show() if on_close else dlg.exec()
55
+
56
+ @classmethod
57
+ def question(cls, parent: Widget, title: str, message: str, on_close: Callable[[Any], None]) -> bool:
58
+ return cls._dialog(parent=parent, title=title, message=message, icon=Style.StandardPixmap.SP_MESSAGEBOXQUESTION, on_close=on_close)
59
+
60
+ @classmethod
61
+ def warning(cls, parent: Widget, title: str, message: str, on_close: Callable[[Any], None]):
62
+ return cls._dialog(parent=parent, title=title, message=message, icon=Style.StandardPixmap.SP_MESSAGEBOXWARNING, on_close=on_close)
63
+
64
+ @classmethod
65
+ def information(cls, parent: Widget, title: str, message: str, on_close: Callable[[Any], None]):
66
+ return cls._dialog(parent=parent, title=title, message=message, icon=Style.StandardPixmap.SP_MESSAGEBOXINFORMATION, on_close=on_close)
67
+
68
+ @classmethod
69
+ def is_yes_button(cls, sb) -> bool:
70
+ return sb
@@ -0,0 +1,31 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Literal
4
+
5
+ from core_10x.named_constant import Enum
6
+
7
+ import rio
8
+ import ui_10x.platform_interface as i
9
+ from ui_10x.rio.component_builder import Widget
10
+
11
+
12
+ class SCROLL(Enum):
13
+ OFF = 'never'
14
+ ON = 'always'
15
+ AS_NEEDED = 'auto'
16
+
17
+
18
+ class ScrollArea(Widget, i.ScrollArea):
19
+ s_component_class = rio.ScrollContainer
20
+
21
+ def set_widget(self, w: Widget):
22
+ self.set_children([w])
23
+
24
+ def set_horizontal_scroll_bar_policy(self, h):
25
+ self._set_scrollbar_policy('scroll_x', h)
26
+
27
+ def set_vertical_scroll_bar_policy(self, h):
28
+ self._set_scrollbar_policy('scroll_y', h)
29
+
30
+ def _set_scrollbar_policy(self, scroll: Literal['scroll_x', 'scroll_y'], policy: SCROLL):
31
+ self[scroll] = policy.label
@@ -0,0 +1,6 @@
1
+ import rio
2
+ from ui_10x.rio.component_builder import Widget
3
+
4
+
5
+ class Spacer(Widget):
6
+ s_component_class = rio.Spacer
@@ -0,0 +1,45 @@
1
+ from __future__ import annotations
2
+
3
+ from core_10x.named_constant import Enum
4
+
5
+ import ui_10x.platform_interface as i
6
+ import ui_10x.rio.components as rio_components
7
+ from ui_10x.rio.component_builder import Widget
8
+
9
+
10
+ class Direction(Enum):
11
+ VERTICAL = 'vertical'
12
+ HORIZONTAL = 'horizontal'
13
+
14
+
15
+ Horizontal = Direction.HORIZONTAL
16
+
17
+
18
+ class Splitter(Widget, i.Splitter):
19
+ s_component_class = rio_components.Splitter
20
+ s_pass_children_in_kwargs = True
21
+
22
+ def _make_kwargs(self, **kwargs):
23
+ kwargs = super()._make_kwargs(**kwargs)
24
+ if kwargs['direction'] == Horizontal.label:
25
+ del kwargs['align_y']
26
+ kwargs['grow_y'] = True
27
+ kwargs['child_proportions'] = []
28
+ return kwargs
29
+
30
+ def __init__(self, direction: Direction = Horizontal, **kwargs):
31
+ super().__init__(**kwargs | dict(direction=direction.label))
32
+
33
+ def add_widget(self, widget: Widget):
34
+ self.add_children(widget)
35
+ self['child_proportions'].append(1)
36
+
37
+ def set_handle_width(self, width: int):
38
+ self['handle_size'] = width
39
+
40
+ def set_stretch_factor(self, widget_index: int, factor: int):
41
+ self['child_proportions'][widget_index] = factor # TODO: normalize?
42
+
43
+ def replace_widget(self, widget_index: int, widget: Widget):
44
+ self.get_children()[widget_index] = widget
45
+ self.force_update()
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ import rio
4
+ import ui_10x.platform_interface as i
5
+ from ui_10x.rio.component_builder import Widget
6
+
7
+
8
+ class TextEdit(Widget, i.TextEdit):
9
+ s_component_class = rio.MultiLineTextInput
10
+
11
+ def _make_kwargs(self, **kwargs):
12
+ kw = super()._make_kwargs(**kwargs)
13
+ handler = self.focus_out_event
14
+ if handler.__func__ != TextEdit.focus_out_event: # do not set unless implemented
15
+ kw['on_lose_focus'] = self.callback(handler)
16
+ # TODO: test clipboard interactions
17
+ return kw
18
+
19
+ def to_plain_text(self) -> str:
20
+ return self['text']
21
+
22
+ def set_plain_text(self, text: str):
23
+ self['text'] = text
24
+
25
+ def set_read_only(self, readonly: bool):
26
+ self['is_sensitive'] = not readonly
27
+
28
+ def focus_out_event(self, event): ...
@@ -0,0 +1,89 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import partial
4
+
5
+ import ui_10x.platform_interface as i
6
+ import ui_10x.rio.components as rio_components
7
+ from ui_10x.rio.component_builder import Widget
8
+
9
+
10
+ class TreeItem(Widget, i.TreeItem):
11
+ __slots__ = ('handlers',)
12
+ s_component_class = rio_components.RioTreeItem
13
+ s_pass_children_in_kwargs = True
14
+
15
+ def __init__(self, parent: TreeWidget | TreeItem, *args, **kwargs):
16
+ super().__init__(*args, **kwargs)
17
+ parent[self.s_children_attr] = parent[self.s_children_attr] + [self]
18
+ self.handlers = parent.handlers
19
+ for name, callback in self.handlers.items():
20
+ self[name.replace('_item_', '_')] = partial(callback, self)
21
+
22
+ def set_expanded(self, expanded: bool):
23
+ self['is_expanded'] = expanded
24
+
25
+ # noinspection PyMethodOverriding
26
+ def set_text(self, col: int, text: str):
27
+ super().set_text(text)
28
+
29
+ # noinspection PyMethodOverriding
30
+ def set_tool_tip(self, col: int, text: str):
31
+ super().set_tool_tip(text)
32
+
33
+
34
+ class TreeWidget(Widget, i.TreeWidget):
35
+ __slots__ = ('handlers',)
36
+ s_component_class = rio_components.RioTreeView
37
+ s_default_kwargs = dict(selection_mode='single')
38
+
39
+ def __init__(self, *args, **kwargs):
40
+ super().__init__(*args, **kwargs)
41
+ self.handlers = {}
42
+
43
+ def set_column_count(self, col_count: int):
44
+ """Set the number of columns in the tree widget."""
45
+ assert col_count in [1, 2], 'col_count must be 1 or 2'
46
+ self['col_count'] = col_count
47
+
48
+ def set_header_labels(self, labels: list):
49
+ """Set the header labels for each column."""
50
+ self['header_labels'] = labels
51
+
52
+ def top_level_item_count(self) -> int:
53
+ """Return the number of top-level items."""
54
+ return len(self.get_children())
55
+
56
+ def top_level_item(self, i: int) -> TreeItem:
57
+ """Return the top-level item at index i."""
58
+ return self.get_children()[i]
59
+
60
+ def resize_column_to_contents(self, col: int):
61
+ """Adjust the width of the specified column (placeholder)."""
62
+ pass
63
+
64
+ def item_expanded_connect(self, bound_method):
65
+ self.handlers['on_item_expand'] = self.callback(bound_method)
66
+
67
+ def item_clicked_connect(self, bound_method):
68
+ # self.handlers['on_item_double_press'] = bound_method
69
+ self.handlers['on_item_press'] = self.callback(bound_method)
70
+
71
+ def item_pressed_connect(self, bound_method):
72
+ self.handlers['on_item_press'] = bound_method
73
+
74
+ def item_changed_connect(self, bound_method):
75
+ raise NotImplementedError
76
+
77
+ def edit_item(self, item: TreeItem, col: int):
78
+ """Start editing the specified item in the given column (placeholder)."""
79
+ # self.component.start_editing(item, col)
80
+ raise NotImplementedError
81
+
82
+ def open_persistent_editor(self, item: TreeItem, col: int):
83
+ """Open a persistent editor for the specified item and column (placeholder)."""
84
+ # self.component.open_persistent_editor(item, col)
85
+ raise NotImplementedError
86
+
87
+ def add_top_level_item(self, item: TreeItem):
88
+ """Add a top-level item to the tree (helper method)."""
89
+ self.get_children().append(item)
@@ -0,0 +1,101 @@
1
+ import asyncio
2
+
3
+ import rio.testing.browser_client
4
+ from ui_10x.rio.component_builder import DynamicComponent
5
+ from ui_10x.rio.widgets import PushButton
6
+
7
+
8
+ async def test_button_comprehensive() -> None:
9
+ """Test PushButton with comprehensive client-widget interaction verification."""
10
+ widget = PushButton('Click Me')
11
+ find_button_text = 'document.querySelector(".rio-button .rio-text").children[0].innerText'
12
+
13
+ clicked_calls = []
14
+
15
+ def clicked_handler():
16
+ clicked_calls.append(True)
17
+
18
+ widget.clicked_connect(clicked_handler)
19
+
20
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
21
+ await asyncio.sleep(0.5)
22
+
23
+ # 1) Verify client shows widget value
24
+ assert widget['content'] == 'Click Me'
25
+ client_text = await test_client.execute_js(find_button_text)
26
+ assert client_text == 'Click Me'
27
+
28
+ # 2) Modify client value (user clicking button)
29
+ await test_client.click(10, 10) # Click on the button
30
+
31
+ # 3) Verify widget reflects client value (click handler called)
32
+ assert len(clicked_calls) == 1
33
+ assert clicked_calls[0] is True
34
+
35
+ # Test another click
36
+ await test_client.click(10, 10)
37
+ assert len(clicked_calls) == 2
38
+
39
+
40
+ async def test_button_disabled_interaction() -> None:
41
+ """Test PushButton disabled state blocks user interaction."""
42
+ widget = PushButton('Test Button')
43
+
44
+ clicked_calls = []
45
+
46
+ def clicked_handler():
47
+ clicked_calls.append(True)
48
+
49
+ widget.clicked_connect(clicked_handler)
50
+
51
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
52
+ await asyncio.sleep(0.5)
53
+
54
+ # Test initial enabled state - clicks should work
55
+ await test_client.click(10, 10)
56
+ assert len(clicked_calls) == 1
57
+
58
+ # Disable widget
59
+ widget.set_enabled(False)
60
+ await test_client.wait_for_refresh()
61
+ assert not widget['is_sensitive']
62
+
63
+ # Test that clicks are blocked when disabled
64
+ await test_client.click(10, 10)
65
+ assert len(clicked_calls) == 1 # No additional calls
66
+
67
+ # Re-enable widget
68
+ widget.set_enabled(True)
69
+ await test_client.wait_for_refresh()
70
+ assert widget['is_sensitive']
71
+
72
+ # Test that clicks work again
73
+ await test_client.click(10, 10)
74
+ assert len(clicked_calls) == 2
75
+
76
+
77
+ async def test_button_basic_functionality() -> None:
78
+ """Test PushButton basic functionality."""
79
+ widget = PushButton('Test Button')
80
+
81
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
82
+ await asyncio.sleep(0.5)
83
+
84
+ # Test initial state
85
+ assert widget['content'] == 'Test Button'
86
+ assert widget['is_sensitive']
87
+
88
+ # Test enabled/disabled state with timeout protection
89
+ widget.set_enabled(False)
90
+ await test_client.wait_for_refresh()
91
+ assert not widget['is_sensitive']
92
+
93
+ widget.set_enabled(True)
94
+ await test_client.wait_for_refresh()
95
+ assert widget['is_sensitive']
96
+
97
+ # Test flat style changes with timeout protection
98
+ assert not await test_client.execute_js('document.querySelector(".rio-buttonstyle-plain-text")')
99
+ widget.set_flat(True)
100
+ await test_client.wait_for_refresh()
101
+ assert await test_client.execute_js('document.querySelector(".rio-buttonstyle-plain-text")')
@@ -0,0 +1,33 @@
1
+ import asyncio
2
+
3
+ import rio.testing.browser_client
4
+ from ui_10x.rio.component_builder import DynamicComponent
5
+ from ui_10x.rio.widgets.button_group import ButtonGroup, RadioButton
6
+
7
+
8
+ async def test_button_group() -> None:
9
+ group = ButtonGroup()
10
+ btn1 = RadioButton(label='A', value='A')
11
+ btn2 = RadioButton(label='B', value='B')
12
+ group.add_button(btn1, 0)
13
+ group.add_button(btn2, 1)
14
+
15
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(group)) as test_client:
16
+ await asyncio.sleep(0.5)
17
+ # Initially, nothing selected
18
+ assert group.checked_id() == -1
19
+
20
+ # Select first button
21
+ btn1.set_checked(True)
22
+ await test_client.wait_for_refresh()
23
+ assert group.checked_id() == 0
24
+
25
+ # Select second button
26
+ btn2.set_checked(True)
27
+ await test_client.wait_for_refresh()
28
+ assert group.checked_id() == 1
29
+
30
+ # Deselect second button
31
+ btn2.set_checked(False)
32
+ await test_client.wait_for_refresh()
33
+ assert group.checked_id() == -1
@@ -0,0 +1,114 @@
1
+ import asyncio
2
+ from datetime import date
3
+
4
+ import dateutil.parser
5
+ import rio.testing
6
+ from ui_10x.rio.component_builder import DynamicComponent
7
+ from ui_10x.rio.widgets import CalendarWidget
8
+
9
+ find_selected_date = (
10
+ 'document.querySelector(".rio-calendar-selected-day").textContent + " " + document.querySelector(".rio-calendar-year-month-display").textContent'
11
+ )
12
+
13
+
14
+ async def verify_content(widget, test_client):
15
+ selected_date = await test_client.execute_js(find_selected_date)
16
+
17
+ assert dateutil.parser.parse(selected_date).date() == widget.selected_date()
18
+
19
+
20
+ async def test_calendar_comprehensive() -> None:
21
+ """Test CalendarWidget with comprehensive client-widget interaction verification."""
22
+ widget = CalendarWidget()
23
+ test_date = date(2024, 6, 15)
24
+
25
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
26
+ await asyncio.sleep(0.5)
27
+
28
+ # 1) Verify client shows widget value (initial state)
29
+ assert widget.selected_date() == date.today()
30
+
31
+ # Verify client shows the expected date
32
+ await verify_content(widget, test_client)
33
+
34
+ # 2) Modify widget value and verify client reflects it
35
+ widget.set_selected_date(test_date)
36
+ await test_client.wait_for_refresh()
37
+ assert widget.selected_date() == test_date
38
+
39
+ # Verify client shows the expected date
40
+ await verify_content(widget, test_client)
41
+
42
+ # Test another date change
43
+ new_date = date(2024, 8, 20)
44
+ widget.set_selected_date(new_date)
45
+ await test_client.wait_for_refresh()
46
+ assert widget.selected_date() == new_date
47
+
48
+ # Verify client shows the new expected date
49
+ await verify_content(widget, test_client)
50
+
51
+
52
+ async def test_calendar_disabled_interaction() -> None:
53
+ """Test CalendarWidget disabled state."""
54
+ widget = CalendarWidget()
55
+
56
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
57
+ await asyncio.sleep(0.5)
58
+
59
+ # Test initial enabled state
60
+ assert widget['is_sensitive']
61
+
62
+ # Disable widget
63
+ widget.set_enabled(False)
64
+ await test_client.wait_for_refresh()
65
+ assert not widget['is_sensitive']
66
+ await verify_content(widget, test_client)
67
+
68
+ # Test that date changes still work (programmatic changes are allowed)
69
+ widget.set_selected_date(date(2024, 12, 25))
70
+ await test_client.wait_for_refresh()
71
+ assert widget.selected_date() == date(2024, 12, 25)
72
+
73
+ # Verify client shows the expected date even when disabled
74
+ await verify_content(widget, test_client)
75
+
76
+ # Re-enable widget
77
+ widget.set_enabled(True)
78
+ await test_client.wait_for_refresh()
79
+ assert widget['is_sensitive']
80
+
81
+ # Test that date changes continue to work
82
+ widget.set_selected_date(date(2024, 1, 1))
83
+ await test_client.wait_for_refresh()
84
+ assert widget.selected_date() == date(2024, 1, 1)
85
+
86
+ # Verify client shows the final expected date
87
+ await verify_content(widget, test_client)
88
+
89
+
90
+ async def test_calendar_basic_functionality() -> None:
91
+ """Test CalendarWidget basic functionality."""
92
+ widget = CalendarWidget()
93
+
94
+ async with rio.testing.DummyClient(lambda: DynamicComponent(widget)) as test_client:
95
+ await asyncio.sleep(0.5)
96
+
97
+ # Test initial state
98
+ assert widget is not None
99
+ assert widget['is_sensitive']
100
+
101
+ # Test date setting
102
+ test_date = date(2024, 3, 10)
103
+ widget.set_selected_date(test_date)
104
+ await test_client.wait_for_refresh()
105
+ assert widget.selected_date() == test_date
106
+
107
+ # Test enabled/disabled state
108
+ widget.set_enabled(False)
109
+ await test_client.wait_for_refresh()
110
+ assert not widget['is_sensitive']
111
+
112
+ widget.set_enabled(True)
113
+ await test_client.wait_for_refresh()
114
+ assert widget['is_sensitive']
@@ -0,0 +1,109 @@
1
+ import asyncio
2
+
3
+ import rio.testing
4
+ from ui_10x.rio.component_builder import DynamicComponent
5
+ from ui_10x.rio.widgets import CheckBox
6
+
7
+
8
+ async def test_checkbox_comprehensive() -> None:
9
+ """Test CheckBox with comprehensive client-widget interaction verification."""
10
+ widget = CheckBox('Check me')
11
+ find_checkbox = 'document.querySelector("input[type=\'checkbox\']")'
12
+
13
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
14
+ await asyncio.sleep(0.5)
15
+
16
+ # 1) Verify client shows widget value (unchecked initially)
17
+ assert not widget.is_checked()
18
+ client_checked = await test_client.execute_js(find_checkbox + '.checked')
19
+ assert client_checked is False
20
+
21
+ # 2) Modify client value (user clicking checkbox)
22
+ await test_client.execute_js(find_checkbox + '.click();')
23
+ await asyncio.sleep(0.5) # Wait for event processing
24
+
25
+ # 3) Verify widget reflects client value
26
+ assert widget.is_checked()
27
+ client_checked = await test_client.execute_js(find_checkbox + '.checked')
28
+ assert client_checked is True
29
+
30
+ # Test widget changes propagate to client
31
+ widget.set_checked(False)
32
+ await test_client.wait_for_refresh()
33
+ client_checked = await test_client.execute_js(find_checkbox + '.checked')
34
+ assert client_checked is False
35
+ assert not widget.is_checked()
36
+
37
+ # Test another client interaction
38
+ await test_client.execute_js(find_checkbox + '.click();')
39
+ await asyncio.sleep(0.5)
40
+ assert widget.is_checked()
41
+
42
+
43
+ async def test_checkbox_disabled_interaction() -> None:
44
+ """Test CheckBox disabled state blocks user interaction."""
45
+ widget = CheckBox('Test Checkbox')
46
+ find_checkbox = 'document.querySelector("input[type=\'checkbox\']")'
47
+
48
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
49
+ await asyncio.sleep(0.5)
50
+
51
+ # Test initial enabled state - clicks should work
52
+ await test_client.execute_js(find_checkbox + '.click();')
53
+ await asyncio.sleep(0.5)
54
+ assert widget.is_checked()
55
+
56
+ # Disable widget
57
+ widget.set_enabled(False)
58
+ await test_client.wait_for_refresh()
59
+
60
+ # Verify client shows disabled state
61
+ client_disabled = await test_client.execute_js(find_checkbox + '.disabled')
62
+ assert client_disabled is True
63
+
64
+ # Test that clicks are blocked when disabled
65
+ initial_checked = widget.is_checked()
66
+ await test_client.execute_js(find_checkbox + '.click();')
67
+ await asyncio.sleep(0.5)
68
+ assert widget.is_checked() == initial_checked # State should not change
69
+
70
+ # Re-enable widget
71
+ widget.set_enabled(True)
72
+ await test_client.wait_for_refresh()
73
+
74
+ # Verify client shows enabled state
75
+ client_disabled = await test_client.execute_js(find_checkbox + '.disabled')
76
+ assert client_disabled is False
77
+
78
+ # Test that clicks work again
79
+ await test_client.execute_js(find_checkbox + '.click();')
80
+ await asyncio.sleep(0.5)
81
+ assert not widget.is_checked() # Should toggle
82
+
83
+
84
+ async def test_checkbox_basic_functionality() -> None:
85
+ """Test CheckBox basic functionality without client interaction."""
86
+ widget = CheckBox('Test Checkbox')
87
+
88
+ async with rio.testing.BrowserClient(lambda: DynamicComponent(widget)) as test_client:
89
+ await asyncio.sleep(0.5)
90
+
91
+ # Test initial state
92
+ assert not widget.is_checked()
93
+
94
+ # Test checking
95
+ widget.set_checked(True)
96
+ await test_client.wait_for_refresh()
97
+ assert widget.is_checked()
98
+
99
+ # Test unchecking
100
+ widget.set_checked(False)
101
+ await test_client.wait_for_refresh()
102
+ assert not widget.is_checked()
103
+
104
+ # Test enabled/disabled state
105
+ widget.set_enabled(False)
106
+ await test_client.wait_for_refresh()
107
+
108
+ widget.set_enabled(True)
109
+ await test_client.wait_for_refresh()