ren-ui-python 0.1.0__tar.gz → 0.2.0__tar.gz

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 (30) hide show
  1. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/PKG-INFO +1 -1
  2. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/README.md +33 -33
  3. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/pyproject.toml +1 -1
  4. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/__init__.py +1 -1
  5. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/app.py +4 -4
  6. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/widgets.py +19 -19
  7. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui_python.egg-info/PKG-INFO +1 -1
  8. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/tests/test_samples.py +3 -3
  9. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/tests/test_widgets.py +4 -4
  10. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/LICENSE +0 -0
  11. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/PYPI.md +0 -0
  12. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/setup.cfg +0 -0
  13. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/__main__.py +0 -0
  14. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/_native.py +0 -0
  15. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/build.py +0 -0
  16. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/cli.py +0 -0
  17. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/native/bridge.cpp +0 -0
  18. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/native/icon.cpp +0 -0
  19. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/native/launcher.cpp +0 -0
  20. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/project.py +0 -0
  21. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/py.typed +0 -0
  22. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/resources.py +0 -0
  23. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui/system.py +0 -0
  24. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui_python.egg-info/SOURCES.txt +0 -0
  25. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui_python.egg-info/dependency_links.txt +0 -0
  26. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui_python.egg-info/entry_points.txt +0 -0
  27. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/src/ren_ui_python.egg-info/top_level.txt +0 -0
  28. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/tests/test_build.py +0 -0
  29. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/tests/test_images.py +0 -0
  30. {ren_ui_python-0.1.0 → ren_ui_python-0.2.0}/tests/test_project.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ren-ui-python
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Native RenkuOS/HaikuOS desktop apps in Python, packaged as HPKG
5
5
  Author-email: Lee JunHaeng aka rainygirl <rainygirl@gmail.com>
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@
3
3
  Python 코드로 **RenkuOS 네이티브 UI와 앱을 작성하고 `.hpkg`로 패키징**하는 프레임워크입니다.
4
4
  웹뷰나 다른 OS의 위젯을 흉내 내지 않고, C++ 브리지를 통해 Haiku의 `libbe`를 호출합니다.
5
5
 
6
- 현재 버전은 **0.1.0 초기 구현**입니다. 아래에 명시한 API를 지원하며, Haiku API 전체에 대한 바인딩은 아닙니다.
6
+ 현재 버전은 **0.2.0 초기 구현**입니다. 아래에 명시한 API를 지원하며, Haiku API 전체에 대한 바인딩은 아닙니다.
7
7
  Python 3.11 이상이 필요합니다. macOS/Linux에서는 프로젝트 생성·검사·소스 스테이징·단위 테스트가 가능하고,
8
8
  네이티브 UI 실행과 실제 HPKG 빌드는 Haiku에서 수행합니다.
9
9
 
@@ -12,10 +12,10 @@ Python 3.11 이상이 필요합니다. macOS/Linux에서는 프로젝트 생성
12
12
 
13
13
  ## 빠른 시작
14
14
 
15
- 저장소 루트에서 설치합니다.
15
+ [PyPI](https://pypi.org/project/ren-ui-python/)에서 설치합니다.
16
16
 
17
17
  ```sh
18
- python3 -m pip install .
18
+ python3 -m pip install ren-ui-python
19
19
  ren init hello_haiku
20
20
  ren check hello_haiku
21
21
  ```
@@ -52,8 +52,8 @@ python3 -m ren_ui build examples/hello
52
52
  ## 샘플 앱
53
53
 
54
54
  각 디렉터리에 앱 코드, `ren.toml`, `LICENSE`가 들어 있습니다.
55
- 저장소 루트에서 `python3 -m pip install .`로 현재 프레임워크를 설치한 뒤 Haiku에서 실행하세요.
56
- 이전에 설치한 경우에도 다시 설치해야 새 `TextArea`와 `confirm` API를 사용할 수 있습니다.
55
+ `python3 -m pip install ren-ui-python`으로 프레임워크를 설치한 뒤 Haiku에서 실행하세요.
56
+ 이전에 설치했다면 `python3 -m pip install --upgrade ren-ui-python`으로 최신 버전으로 올립니다.
57
57
 
58
58
  | 샘플 | 코드 | 배우는 내용 |
59
59
  | --- | --- | --- |
@@ -142,7 +142,7 @@ class DialogDemo:
142
142
  ),
143
143
  width=560, height=240,
144
144
  # 메인 창을 닫으면 열려 있는 입력 창도 함께 종료합니다.
145
- on_close=self.quit,
145
+ onclosed=self.quit,
146
146
  )
147
147
 
148
148
  def quit(self, event: Event):
@@ -195,7 +195,7 @@ class DialogDemo:
195
195
  hint, name,
196
196
  Row(apply_button, Button("취소", cancel), padding=0),
197
197
  ),
198
- width=400, height=180, resizable=False, on_close=closed,
198
+ width=400, height=180, resizable=False, onclosed=closed,
199
199
  )
200
200
  self.input_window = dialog.show()
201
201
 
@@ -375,7 +375,7 @@ class EditorDemo:
375
375
  self.draft_text: str | None = None
376
376
  self.draft_error: str | None = None
377
377
  self.path = TextInput(label="파일 경로:")
378
- self.editor = TextArea(on_change=self.remember_change)
378
+ self.editor = TextArea(onchange=self.remember_change)
379
379
  self.status = Label("파일의 전체 경로를 입력하고 열기 또는 저장을 누르세요.")
380
380
  self.window = Window(
381
381
  app, "Ren · 텍스트 에디터",
@@ -612,12 +612,12 @@ with Application("application/x-vnd.example-hello") as app:
612
612
  | `system.settings_directory` | `find_directory` | 사용자 설정 디렉터리 |
613
613
 
614
614
  버튼은 `@button.onclick` 데코레이터로 클릭 함수를 등록합니다. 생성자의 `onclick` 인자로 전달해도 됩니다.
615
- 입력·체크박스·슬라이더는 `on_change`에 함수를 전달합니다.
615
+ 입력·체크박스·슬라이더는 `onchange`에 함수를 전달합니다.
616
616
  콜백 인자는 `Event(source, kind, text, value)`입니다. `text`와 `value`는 이벤트 발생 시점의 스냅샷입니다.
617
617
  `TextInput`은 편집 및 Enter 입력 시, 슬라이더는 드래그 및 확정 시 알림을 보내므로 같은 값의 이벤트가 반복될 수 있습니다.
618
618
  `TextArea`는 편집 시와 표시된 위젯의 `.text`에 대입할 때 변경 이벤트를 보냅니다.
619
619
  `@window.onclose`는 닫기 전 확인 함수이며 `True`를 반환해야 닫힙니다.
620
- 기존 `Window.on_close`는 창이 이미 닫힌 후 호출되는 별도의 알림입니다.
620
+ 기존 `Window.onclosed`는 창이 이미 닫힌 후 호출되는 별도의 알림입니다.
621
621
  마지막 창을 닫으면 앱도 종료됩니다.
622
622
 
623
623
  ## 문법과 API 사용법
@@ -729,7 +729,7 @@ app.call_soon(started)
729
729
  ### Window: 창 구성
730
730
 
731
731
  ```text
732
- Window(app, title, content, *, width=480, height=280, resizable=True, onclose=None, on_close=None)
732
+ Window(app, title, content, *, width=480, height=280, resizable=True, onclose=None, onclosed=None)
733
733
  ```
734
734
 
735
735
  `content`에는 위젯 또는 레이아웃 하나를 전달합니다. 여러 위젯을 넣으려면 `Column`이나 `Row`로 묶습니다.
@@ -750,7 +750,7 @@ window = Window(
750
750
  width=520,
751
751
  height=320,
752
752
  resizable=False,
753
- on_close=closed,
753
+ onclosed=closed,
754
754
  )
755
755
  window.show()
756
756
  ```
@@ -784,13 +784,13 @@ def before_close(event: Event) -> bool:
784
784
  창 테두리의 닫기 버튼과 표시된 창의 `window.close()` 모두 이 확인을 거칩니다.
785
785
  확인 중 중복 요청은 합치며, 핸들러는 네이티브 창 잠금을 해제한 Python 이벤트 처리 스레드에서 실행됩니다.
786
786
  핸들러 안에서는 `window.close()`를 다시 호출할 필요 없이 결과만 반환하세요.
787
- `window.close()`는 실제 닫힘을 기다리지 않습니다. 완료 처리는 `on_close`에서 수행합니다.
787
+ `window.close()`는 실제 닫힘을 기다리지 않습니다. 완료 처리는 `onclosed`에서 수행합니다.
788
788
  `app.quit()`도 각 창의 확인을 거칩니다. 허용한 창은 닫히고 취소한 창은 남으며, 모든 창이 닫힌 뒤 앱이 종료됩니다.
789
789
  종료 버튼에서 특정 창만 닫으려면 `event: Event`를 받는 핸들러 안에서 `window.close()`를 호출하세요.
790
790
  실행 전·종료 후 자원 정리인 `app.close()`는 확인 없이 자원을 해제합니다.
791
791
  아직 `show()`하지 않은 창의 `close()`는 네이티브 창이 없으므로 핸들러 없이 객체를 닫습니다.
792
792
 
793
- `on_close`는 닫기 완료 알림이므로 콜백에서 그 창의 컨트롤을 수정하지 않습니다.
793
+ `onclosed`는 닫기 완료 알림이므로 콜백에서 그 창의 컨트롤을 수정하지 않습니다.
794
794
  콜백의 반환값으로 닫기를 취소할 수 없습니다.
795
795
  `width`, `height`, `resizable`, `content`는 표시 전 구성에 사용하며,
796
796
  창 표시 후 해당 필드에 대입하는 방식으로 창 크기나 내용을 변경하는 API는 제공하지 않습니다.
@@ -847,10 +847,10 @@ content = Column(*labels)
847
847
  ```text
848
848
  Label(text="")
849
849
  Button(text, onclick=None, *, enabled=True, icon=None, icon_size=24)
850
- TextInput(text="", *, label="", on_change=None, enabled=True)
851
- TextArea(text="", *, on_change=None, editable=True, wrap=True)
852
- CheckBox(text, *, checked=False, on_change=None, enabled=True)
853
- Slider(text="", *, minimum=0, maximum=100, value=0, on_change=None)
850
+ TextInput(text="", *, label="", onchange=None, enabled=True)
851
+ TextArea(text="", *, onchange=None, editable=True, wrap=True)
852
+ CheckBox(text, *, checked=False, onchange=None, enabled=True)
853
+ Slider(text="", *, minimum=0, maximum=100, value=0, onchange=None)
854
854
  ```
855
855
 
856
856
  | 위젯 | 인자의 의미 | 생성 예 |
@@ -878,7 +878,7 @@ from ren_ui import Event, TextArea
878
878
  def document_changed(event: Event):
879
879
  print("변경된 내용:", event.text)
880
880
 
881
- editor = TextArea("첫 줄\n둘째 줄", on_change=document_changed)
881
+ editor = TextArea("첫 줄\n둘째 줄", onchange=document_changed)
882
882
  preview = TextArea("선택·복사 가능한 읽기 전용 텍스트", editable=False)
883
883
  ```
884
884
 
@@ -944,9 +944,9 @@ def checked_changed(event: Event):
944
944
  def volume_changed(event: Event):
945
945
  print("음량:", event.value)
946
946
 
947
- name = TextInput(label="이름:", on_change=text_changed)
948
- enabled = CheckBox("사용", on_change=checked_changed)
949
- volume = Slider("음량", on_change=volume_changed)
947
+ name = TextInput(label="이름:", onchange=text_changed)
948
+ enabled = CheckBox("사용", onchange=checked_changed)
949
+ volume = Slider("음량", onchange=volume_changed)
950
950
  ```
951
951
 
952
952
  `Event`는 읽기 전용 객체입니다. 처리 함수의 반환값은 사용하지 않습니다.
@@ -1051,7 +1051,7 @@ def main():
1051
1051
 
1052
1052
  content = Column(
1053
1053
  result,
1054
- CheckBox("증가 버튼 사용", checked=True, on_change=toggle),
1054
+ CheckBox("증가 버튼 사용", checked=True, onchange=toggle),
1055
1055
  Row(increment_button, Button("초기화", reset), padding=0),
1056
1056
  Button("종료", quit_app),
1057
1057
  )
@@ -1360,7 +1360,7 @@ PNG를 앱 아이콘과 버튼 아이콘으로 사용한 설치 앱입니다.
1360
1360
 
1361
1361
  Python으로 Haiku 네이티브 앱을 만들고 HPKG로 배포하는 방법을 설명합니다. 설치와 첫 앱 작성부터 이벤트 처리, 샘플의 조작법, 패키징까지 순서대로 따라 할 수 있습니다.
1362
1362
 
1363
- **대상 버전:** 0.1.0 / 네이티브 브리지 ABI v5
1363
+ **대상 버전:** 0.2.0 / 네이티브 브리지 ABI v5
1364
1364
  **문서 기준:** 2026-10-05 / Python 3.11 이상
1365
1365
 
1366
1366
  ![Python으로 만든 Haiku 캘린더 앱](docs/verification/arm64-2026-10-05/calendar.png)
@@ -1394,10 +1394,10 @@ Python으로 Haiku 네이티브 앱을 만들고 HPKG로 배포하는 방법을
1394
1394
  | package, rc, xres, resattr, mimeset | HPKG와 앱 리소스 생성 |
1395
1395
  | 실행 중인 Haiku 데스크톱 | 실제 창과 위젯 표시 |
1396
1396
 
1397
- 저장소 루트에서 다음 명령을 실행합니다. 필요한 도구는 사용 중인 Haiku 환경에 맞춰 먼저 설치합니다.
1397
+ 다음 명령을 실행합니다. 필요한 도구는 사용 중인 Haiku 환경에 맞춰 먼저 설치합니다.
1398
1398
 
1399
1399
  ```sh
1400
- python3 -m pip install .
1400
+ python3 -m pip install ren-ui-python
1401
1401
  ren doctor
1402
1402
  ren init manual_demo
1403
1403
  ren check manual_demo
@@ -1550,7 +1550,7 @@ def save(event: Event):
1550
1550
 
1551
1551
  #### 텍스트와 값 변경
1552
1552
 
1553
- 입력창·텍스트 영역·체크박스·슬라이더는 생성자의 `on_change`에 함수를 전달합니다. 이 위젯들에는 `onclick` 데코레이터를 사용하지 않습니다.
1553
+ 입력창·텍스트 영역·체크박스·슬라이더는 생성자의 `onchange`에 함수를 전달합니다. 이 위젯들에는 `onclick` 데코레이터를 사용하지 않습니다.
1554
1554
 
1555
1555
  ```python
1556
1556
  from ren_ui import Event, TextInput, CheckBox
@@ -1558,11 +1558,11 @@ from ren_ui import Event, TextInput, CheckBox
1558
1558
  def name_changed(event: Event):
1559
1559
  print("입력:", event.text)
1560
1560
 
1561
- def option_changed(event: Event):
1561
+ def optionchanged(event: Event):
1562
1562
  print("체크:", bool(event.value))
1563
1563
 
1564
- name = TextInput(label="이름:", on_change=name_changed)
1565
- option = CheckBox("알림", on_change=option_changed)
1564
+ name = TextInput(label="이름:", onchange=name_changed)
1565
+ option = CheckBox("알림", onchange=optionchanged)
1566
1566
  ```
1567
1567
 
1568
1568
  입력 확정이나 드래그 때문에 같은 값의 이벤트가 반복될 수 있습니다. `TextArea.text`에 대입해도 변경 이벤트가 발생하므로, 자신의 변경 핸들러에서 같은 값을 계속 대입하지 않도록 합니다.
@@ -1615,14 +1615,14 @@ def before_close(event: Event) -> bool:
1615
1615
  - 확인 중 중복 닫기 요청은 합칩니다. 핸들러 안에서 다시 `window.close()`를 호출하지 말고 결과를 반환합니다.
1616
1616
  - `window.close()`는 비동기 요청입니다. 호출 직후에 창이 이미 닫혔다고 가정하지 않습니다.
1617
1617
 
1618
- #### onclose와 on_close의 차이
1618
+ #### onclose와 onclosed의 차이
1619
1619
 
1620
1620
  | 이름 | 호출 시점 | 반환값 |
1621
1621
  | --- | --- | --- |
1622
1622
  | onclose | 창이 닫히기 전 | True만 닫기 허용 |
1623
- | on_close | 실제로 닫힌 뒤 | 닫힘을 취소할 수 없음 |
1623
+ | onclosed | 실제로 닫힌 뒤 | 닫힘을 취소할 수 없음 |
1624
1624
 
1625
- 닫힌 후의 정리가 필요하면 생성자의 `on_close=handler`를 사용합니다. 이 알림에서는 이미 닫힌 창의 컨트롤을 수정하지 않습니다.
1625
+ 닫힌 후의 정리가 필요하면 생성자의 `onclosed=handler`를 사용합니다. 이 알림에서는 이미 닫힌 창의 컨트롤을 수정하지 않습니다.
1626
1626
 
1627
1627
  아직 `show()`하지 않은 창의 `close()`와 실행 전·종료 후 자원 정리인 `app.close()`는 확인 없이 정리합니다. 실행 중 종료를 요청할 때는 `window.close()` 또는 `app.quit()`를 사용합니다.
1628
1628
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ren-ui-python"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "Native RenkuOS/HaikuOS desktop apps in Python, packaged as HPKG"
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.11"
@@ -4,7 +4,7 @@ from .app import Application, Event
4
4
  from .resources import asset_path
5
5
  from .widgets import Button, CheckBox, Column, Image, Label, Row, Slider, TextArea, TextInput, Window
6
6
 
7
- __version__ = "0.1.0"
7
+ __version__ = "0.2.0"
8
8
  __all__ = [
9
9
  "Application", "Event", "Image", "asset_path", "Window", "Column", "Row", "Label", "Button",
10
10
  "TextInput", "TextArea", "CheckBox", "Slider",
@@ -84,8 +84,8 @@ class Application:
84
84
  if window._closed:
85
85
  continue
86
86
  window._closed = True
87
- if window.on_close:
88
- window.on_close(Event(window, kind))
87
+ if window.onclosed:
88
+ window.onclosed(Event(window, kind))
89
89
  continue
90
90
  if window._closed or window._closing:
91
91
  continue
@@ -93,8 +93,8 @@ class Application:
93
93
  window._decide_close(Event(window, kind))
94
94
  continue
95
95
  widget = window._widgets.get(view_id)
96
- if widget is not None and widget.on_change:
97
- widget.on_change(Event(widget, kind, text, value))
96
+ if widget is not None and widget.onchange:
97
+ widget.onchange(Event(widget, kind, text, value))
98
98
  except Exception:
99
99
  # A failing handler must not kill the dispatcher or the app.
100
100
  traceback.print_exc()
@@ -19,14 +19,14 @@ def _dimension(value, name, *, positive=False):
19
19
  class Widget:
20
20
  kind = ""
21
21
 
22
- def __init__(self, text="", *, on_change=None, enabled=True):
22
+ def __init__(self, text="", *, onchange=None, enabled=True):
23
23
  utf8(text)
24
- if on_change is not None and not callable(on_change):
25
- raise TypeError("on_change must be callable")
24
+ if onchange is not None and not callable(onchange):
25
+ raise TypeError("onchange must be callable")
26
26
  self._text = text
27
27
  self._value = 0
28
28
  self._enabled = bool(enabled)
29
- self.on_change = on_change
29
+ self.onchange = onchange
30
30
  self._window = None
31
31
  self._id = None
32
32
 
@@ -114,7 +114,7 @@ class Button(Widget):
114
114
 
115
115
  def __init__(self, text, onclick=None, *, enabled=True,
116
116
  icon: str | Path | None = None, icon_size: int = 24):
117
- super().__init__(text, on_change=onclick, enabled=enabled)
117
+ super().__init__(text, onchange=onclick, enabled=enabled)
118
118
  if type(icon_size) is not int or not 1 <= icon_size <= 256:
119
119
  raise ValueError("icon_size must be an integer from 1 to 256")
120
120
  self._icon_size = icon_size
@@ -143,7 +143,7 @@ class Button(Widget):
143
143
  """
144
144
  if not callable(callback):
145
145
  raise TypeError("onclick must be callable")
146
- self.on_change = callback
146
+ self.onchange = callback
147
147
  return callback
148
148
 
149
149
  def click(self):
@@ -198,8 +198,8 @@ class Image(Widget):
198
198
  class TextInput(Widget):
199
199
  kind = "text"
200
200
 
201
- def __init__(self, text="", *, label="", on_change=None, enabled=True):
202
- super().__init__(text, on_change=on_change, enabled=enabled)
201
+ def __init__(self, text="", *, label="", onchange=None, enabled=True):
202
+ super().__init__(text, onchange=onchange, enabled=enabled)
203
203
  utf8(label)
204
204
  self.label = label
205
205
 
@@ -207,15 +207,15 @@ class TextInput(Widget):
207
207
  class TextArea(Widget):
208
208
  """Scrollable UTF-8 text, up to 1 MiB, with native editing and undo.
209
209
 
210
- Changes, including assignments to text after mounting, emit on_change.
210
+ Changes, including assignments to text after mounting, emit onchange.
211
211
  wrap is a construction option; editable can be changed at runtime.
212
212
  """
213
213
  kind = "textarea"
214
214
  MAX_BYTES = 1024 * 1024
215
215
 
216
- def __init__(self, text="", *, on_change=None, editable=True, wrap=True):
216
+ def __init__(self, text="", *, onchange=None, editable=True, wrap=True):
217
217
  self._validate_text(text)
218
- super().__init__(text, on_change=on_change, enabled=editable)
218
+ super().__init__(text, onchange=onchange, enabled=editable)
219
219
  self.wrap = bool(wrap)
220
220
 
221
221
  @staticmethod
@@ -240,8 +240,8 @@ class TextArea(Widget):
240
240
  class CheckBox(Widget):
241
241
  kind = "checkbox"
242
242
 
243
- def __init__(self, text, *, checked=False, on_change=None, enabled=True):
244
- super().__init__(text, on_change=on_change, enabled=enabled)
243
+ def __init__(self, text, *, checked=False, onchange=None, enabled=True):
244
+ super().__init__(text, onchange=onchange, enabled=enabled)
245
245
  self._value = int(bool(checked))
246
246
 
247
247
  @property
@@ -256,8 +256,8 @@ class CheckBox(Widget):
256
256
  class Slider(Widget):
257
257
  kind = "slider"
258
258
 
259
- def __init__(self, text="", *, minimum=0, maximum=100, value=0, on_change=None):
260
- super().__init__(text, on_change=on_change)
259
+ def __init__(self, text="", *, minimum=0, maximum=100, value=0, onchange=None):
260
+ super().__init__(text, onchange=onchange)
261
261
  # The ABI carries layout/range values as floats; keep integers exact.
262
262
  if not all(isinstance(v, int) and abs(v) <= 2**24 for v in (minimum, maximum, value)):
263
263
  raise ValueError("slider bounds and value must be integers within +/- 2**24")
@@ -284,13 +284,13 @@ class Row(Column):
284
284
 
285
285
 
286
286
  class Window:
287
- def __init__(self, app, title, content, *, width=480, height=280, resizable=True, onclose=None, on_close=None):
287
+ def __init__(self, app, title, content, *, width=480, height=280, resizable=True, onclose=None, onclosed=None):
288
288
  utf8(title)
289
289
  if not isinstance(content, Widget):
290
290
  raise TypeError("content must be a widget or layout")
291
- if on_close is not None and not callable(on_close):
292
- raise TypeError("on_close must be callable")
293
- self.app, self.content, self.on_close = app, content, on_close
291
+ if onclosed is not None and not callable(onclosed):
292
+ raise TypeError("onclosed must be callable")
293
+ self.app, self.content, self.onclosed = app, content, onclosed
294
294
  self._title = title
295
295
  self.width = _dimension(width, "width", positive=True)
296
296
  self.height = _dimension(height, "height", positive=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ren-ui-python
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Native RenkuOS/HaikuOS desktop apps in Python, packaged as HPKG
5
5
  Author-email: Lee JunHaeng aka rainygirl <rainygirl@gmail.com>
6
6
  License-Expression: MIT
@@ -84,14 +84,14 @@ class DialogTests(unittest.TestCase):
84
84
  dialog = demo.input_window
85
85
  hint, name, buttons = dialog.content.children
86
86
  name.text = " "
87
- buttons.children[0].on_change(None)
87
+ buttons.children[0].onchange(None)
88
88
  self.assertIn("한 글자", hint.text)
89
89
  self.assertEqual(demo.name, "Haiku")
90
90
  name.text = " 한글 이름 "
91
- buttons.children[0].on_change(None)
91
+ buttons.children[0].onchange(None)
92
92
  self.assertEqual(demo.name, "한글 이름")
93
93
  self.assertTrue(dialog._closed)
94
- dialog.on_close(Event(dialog, "closed"))
94
+ dialog.onclosed(Event(dialog, "closed"))
95
95
  self.assertIsNone(demo.input_window)
96
96
 
97
97
 
@@ -31,7 +31,7 @@ class WidgetTests(unittest.TestCase):
31
31
  app = Application("application/x-vnd.ren-test")
32
32
  app._native = Mock()
33
33
  events, closed = [], []
34
- window = Window(app, "Close", Label("x"), on_close=closed.append)
34
+ window = Window(app, "Close", Label("x"), onclosed=closed.append)
35
35
  window._id = 1
36
36
  app._windows[1] = window
37
37
 
@@ -119,7 +119,7 @@ class WidgetTests(unittest.TestCase):
119
119
  for invalid in (None, 42):
120
120
  with self.assertRaises(TypeError):
121
121
  button.onclick(invalid)
122
- self.assertIs(button.on_change, callback)
122
+ self.assertIs(button.onchange, callback)
123
123
 
124
124
  def test_composition_does_not_load_native_library(self):
125
125
  with patch("ren_ui.app.Native", side_effect=AssertionError("must stay lazy")):
@@ -273,8 +273,8 @@ class WidgetTests(unittest.TestCase):
273
273
  def test_event_snapshot_and_close_delivery(self):
274
274
  app = Application("application/x-vnd.ren-test")
275
275
  events = []
276
- widget = TextInput(on_change=events.append)
277
- window = Window(app, "Title", widget, on_close=events.append)
276
+ widget = TextInput(onchange=events.append)
277
+ window = Window(app, "Title", widget, onclosed=events.append)
278
278
  app._windows[1] = window
279
279
  window._widgets[2] = widget
280
280
  app._enqueue(1, 2, b"change", "한글".encode(), 7)
File without changes
File without changes
File without changes