baba-gui 0.1.0__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.
- baba/__init__.py +39 -0
- baba/_core.py +410 -0
- baba/_ffi/ffi_build.py +140 -0
- baba/include/baba.h +54 -0
- baba/include/core/types.h +93 -0
- baba/include/core/vec.h +64 -0
- baba/include/layout/layout.h +82 -0
- baba/include/platform/window.h +47 -0
- baba/include/render/painter.h +66 -0
- baba/include/render/vulkan_renderer.h +38 -0
- baba/include/theme/theme.h +68 -0
- baba/include/widgets/button.h +23 -0
- baba/include/widgets/label.h +26 -0
- baba/include/widgets/textbox.h +29 -0
- baba/include/widgets/widget.h +82 -0
- baba/lib/libbaba_macos.a +0 -0
- baba_gui-0.1.0.dist-info/METADATA +476 -0
- baba_gui-0.1.0.dist-info/RECORD +22 -0
- baba_gui-0.1.0.dist-info/WHEEL +5 -0
- baba_gui-0.1.0.dist-info/licenses/LICENSE +674 -0
- baba_gui-0.1.0.dist-info/top_level.txt +2 -0
- tests/test_baba.py +55 -0
baba/__init__.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from ._core import App, Window, Widget, Button, Label, TextBox, Color, Rect, Vec2
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.2"
|
|
6
|
+
__all__ = [
|
|
7
|
+
"App",
|
|
8
|
+
"Window",
|
|
9
|
+
"Widget",
|
|
10
|
+
"Button",
|
|
11
|
+
"Label",
|
|
12
|
+
"TextBox",
|
|
13
|
+
"Color",
|
|
14
|
+
"Rect",
|
|
15
|
+
"Vec2",
|
|
16
|
+
"get_include_dir",
|
|
17
|
+
"get_lib_dir",
|
|
18
|
+
"get_lib_path",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def get_include_dir() -> Path:
|
|
22
|
+
"""Get the directory containing C header files for Baba GUI development."""
|
|
23
|
+
return Path(__file__).parent / "include"
|
|
24
|
+
|
|
25
|
+
def get_lib_dir() -> Path:
|
|
26
|
+
"""Get the directory containing compiled library files."""
|
|
27
|
+
return Path(__file__).parent / "lib"
|
|
28
|
+
|
|
29
|
+
def get_lib_path() -> Path:
|
|
30
|
+
"""Get the path to the compiled library for the current platform."""
|
|
31
|
+
lib_dir = get_lib_dir()
|
|
32
|
+
if sys.platform == "darwin":
|
|
33
|
+
return lib_dir / "libbaba_macos.a"
|
|
34
|
+
elif sys.platform == "win32":
|
|
35
|
+
if (lib_dir / "libbaba_windows.a").exists():
|
|
36
|
+
return lib_dir / "libbaba_windows.a"
|
|
37
|
+
return lib_dir / "baba_windows.lib"
|
|
38
|
+
else:
|
|
39
|
+
return lib_dir / "libbaba_linux.a"
|
baba/_core.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Callable, Optional, Any, List, Tuple
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from ctypes import CFUNCTYPE, c_void_p
|
|
5
|
+
|
|
6
|
+
from ._ffi.ffi_build import ffi, load_baba
|
|
7
|
+
|
|
8
|
+
_lib = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _get_lib():
|
|
12
|
+
global _lib
|
|
13
|
+
if _lib is None:
|
|
14
|
+
_lib = load_baba()
|
|
15
|
+
return _lib
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Vec2:
|
|
19
|
+
def __init__(self, x: float = 0.0, y: float = 0.0):
|
|
20
|
+
self.x = x
|
|
21
|
+
self.y = y
|
|
22
|
+
|
|
23
|
+
def __repr__(self) -> str:
|
|
24
|
+
return f"Vec2({self.x}, {self.y})"
|
|
25
|
+
|
|
26
|
+
def to_c(self):
|
|
27
|
+
return ffi.new("BabaVec2*", (self.x, self.y))[0]
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def from_c(cls, c_vec) -> Vec2:
|
|
31
|
+
return cls(c_vec.x, c_vec.y)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class Rect:
|
|
35
|
+
def __init__(self, x: float = 0, y: float = 0, width: float = 0, height: float = 0):
|
|
36
|
+
self.x = x
|
|
37
|
+
self.y = y
|
|
38
|
+
self.width = width
|
|
39
|
+
self.height = height
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"Rect({self.x}, {self.y}, {self.width}, {self.height})"
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def left(self) -> float:
|
|
46
|
+
return self.x
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def top(self) -> float:
|
|
50
|
+
return self.y
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def right(self) -> float:
|
|
54
|
+
return self.x + self.width
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def bottom(self) -> float:
|
|
58
|
+
return self.y + self.height
|
|
59
|
+
|
|
60
|
+
def to_c(self):
|
|
61
|
+
return ffi.new("BabaRect*", (self.x, self.y, self.width, self.height))[0]
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def from_c(cls, c_rect) -> Rect:
|
|
65
|
+
return cls(c_rect.x, c_rect.y, c_rect.width, c_rect.height)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Color:
|
|
69
|
+
def __init__(self, r: float = 0.0, g: float = 0.0, b: float = 0.0, a: float = 1.0):
|
|
70
|
+
self.r = min(1.0, max(0.0, r))
|
|
71
|
+
self.g = min(1.0, max(0.0, g))
|
|
72
|
+
self.b = min(1.0, max(0.0, b))
|
|
73
|
+
self.a = min(1.0, max(0.0, a))
|
|
74
|
+
|
|
75
|
+
def __repr__(self) -> str:
|
|
76
|
+
return f"Color({self.r:.2f}, {self.g:.2f}, {self.b:.2f}, {self.a:.2f})"
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_hex(cls, hex_color: str) -> Color:
|
|
80
|
+
hex_color = hex_color.lstrip("#")
|
|
81
|
+
if len(hex_color) == 6:
|
|
82
|
+
r = int(hex_color[0:2], 16) / 255.0
|
|
83
|
+
g = int(hex_color[2:4], 16) / 255.0
|
|
84
|
+
b = int(hex_color[4:6], 16) / 255.0
|
|
85
|
+
return cls(r, g, b, 1.0)
|
|
86
|
+
elif len(hex_color) == 8:
|
|
87
|
+
r = int(hex_color[0:2], 16) / 255.0
|
|
88
|
+
g = int(hex_color[2:4], 16) / 255.0
|
|
89
|
+
b = int(hex_color[4:6], 16) / 255.0
|
|
90
|
+
a = int(hex_color[6:8], 16) / 255.0
|
|
91
|
+
return cls(r, g, b, a)
|
|
92
|
+
raise ValueError(f"Invalid hex color: {hex_color}")
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
def from_rgb(cls, r: int, g: int, b: int, a: int = 255) -> Color:
|
|
96
|
+
return cls(r / 255.0, g / 255.0, b / 255.0, a / 255.0)
|
|
97
|
+
|
|
98
|
+
def to_c(self):
|
|
99
|
+
return ffi.new("BabaColor*", (self.r, self.g, self.b, self.a))[0]
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def from_c(cls, c_color) -> Color:
|
|
103
|
+
return cls(c_color.r, c_color.g, c_color.b, c_color.a)
|
|
104
|
+
|
|
105
|
+
WHITE = lambda: Color(1, 1, 1, 1)
|
|
106
|
+
BLACK = lambda: Color(0, 0, 0, 1)
|
|
107
|
+
RED = lambda: Color(1, 0, 0, 1)
|
|
108
|
+
GREEN = lambda: Color(0, 1, 0, 1)
|
|
109
|
+
BLUE = lambda: Color(0, 0, 1, 1)
|
|
110
|
+
TRANSPARENT = lambda: Color(0, 0, 0, 0)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Widget:
|
|
114
|
+
def __init__(self, ptr=None):
|
|
115
|
+
self._ptr = ptr
|
|
116
|
+
self._children: List[Widget] = []
|
|
117
|
+
self._parent: Optional[Widget] = None
|
|
118
|
+
self._callbacks: List[Any] = []
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def ptr(self):
|
|
122
|
+
return self._ptr
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def bounds(self) -> Rect:
|
|
126
|
+
if not self._ptr:
|
|
127
|
+
return Rect()
|
|
128
|
+
lib = _get_lib()
|
|
129
|
+
c_rect = lib.baba_widget_get_bounds(self._ptr)
|
|
130
|
+
return Rect.from_c(c_rect)
|
|
131
|
+
|
|
132
|
+
@bounds.setter
|
|
133
|
+
def bounds(self, rect: Rect):
|
|
134
|
+
if self._ptr:
|
|
135
|
+
lib = _get_lib()
|
|
136
|
+
lib.baba_widget_set_bounds(self._ptr, rect.to_c())
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def visible(self) -> bool:
|
|
140
|
+
if not self._ptr:
|
|
141
|
+
return False
|
|
142
|
+
lib = _get_lib()
|
|
143
|
+
return lib.baba_widget_is_visible(self._ptr)
|
|
144
|
+
|
|
145
|
+
@visible.setter
|
|
146
|
+
def visible(self, value: bool):
|
|
147
|
+
if self._ptr:
|
|
148
|
+
lib = _get_lib()
|
|
149
|
+
lib.baba_widget_set_visible(self._ptr, value)
|
|
150
|
+
|
|
151
|
+
def add_child(self, child: Widget):
|
|
152
|
+
if self._ptr and child._ptr:
|
|
153
|
+
lib = _get_lib()
|
|
154
|
+
lib.baba_widget_add_child(self._ptr, child._ptr)
|
|
155
|
+
self._children.append(child)
|
|
156
|
+
child._parent = self
|
|
157
|
+
|
|
158
|
+
def remove_child(self, child: Widget):
|
|
159
|
+
if self._ptr and child._ptr:
|
|
160
|
+
lib = _get_lib()
|
|
161
|
+
lib.baba_widget_remove_child(self._ptr, child._ptr)
|
|
162
|
+
if child in self._children:
|
|
163
|
+
self._children.remove(child)
|
|
164
|
+
child._parent = None
|
|
165
|
+
|
|
166
|
+
def invalidate(self):
|
|
167
|
+
if self._ptr:
|
|
168
|
+
lib = _get_lib()
|
|
169
|
+
lib.baba_widget_invalidate(self._ptr)
|
|
170
|
+
|
|
171
|
+
def destroy(self):
|
|
172
|
+
if self._ptr:
|
|
173
|
+
lib = _get_lib()
|
|
174
|
+
lib.baba_widget_destroy(self._ptr)
|
|
175
|
+
self._ptr = None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class Button(Widget):
|
|
179
|
+
_click_callbacks = {}
|
|
180
|
+
|
|
181
|
+
def __init__(self, text: str = ""):
|
|
182
|
+
super().__init__()
|
|
183
|
+
lib = _get_lib()
|
|
184
|
+
self._ptr = lib.baba_button_create(text.encode("utf-8"))
|
|
185
|
+
self._text = text
|
|
186
|
+
self._click_handler: Optional[Callable[[Button], None]] = None
|
|
187
|
+
self._id = id(self)
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def text(self) -> str:
|
|
191
|
+
return self._text
|
|
192
|
+
|
|
193
|
+
@text.setter
|
|
194
|
+
def text(self, value: str):
|
|
195
|
+
self._text = value
|
|
196
|
+
if self._ptr:
|
|
197
|
+
lib = _get_lib()
|
|
198
|
+
lib.baba_button_set_text(self._ptr, value.encode("utf-8"))
|
|
199
|
+
|
|
200
|
+
def on_click(self, callback: Callable[[Button], None]):
|
|
201
|
+
self._click_handler = callback
|
|
202
|
+
Button._click_callbacks[self._id] = (self, callback)
|
|
203
|
+
|
|
204
|
+
lib = _get_lib()
|
|
205
|
+
|
|
206
|
+
@ffi.callback("void(BabaWidget*, void*)")
|
|
207
|
+
def _callback(widget_ptr, user_data):
|
|
208
|
+
if self._click_handler:
|
|
209
|
+
self._click_handler(self)
|
|
210
|
+
|
|
211
|
+
self._callbacks.append(_callback)
|
|
212
|
+
lib.baba_button_set_on_click(self._ptr, _callback, ffi.NULL)
|
|
213
|
+
|
|
214
|
+
def destroy(self):
|
|
215
|
+
if self._id in Button._click_callbacks:
|
|
216
|
+
del Button._click_callbacks[self._id]
|
|
217
|
+
super().destroy()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class Label(Widget):
|
|
221
|
+
def __init__(self, text: str = ""):
|
|
222
|
+
super().__init__()
|
|
223
|
+
lib = _get_lib()
|
|
224
|
+
self._ptr = lib.baba_label_create(text.encode("utf-8"))
|
|
225
|
+
self._text = text
|
|
226
|
+
self._color = Color.BLACK()
|
|
227
|
+
self._font_size = 14.0
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def text(self) -> str:
|
|
231
|
+
return self._text
|
|
232
|
+
|
|
233
|
+
@text.setter
|
|
234
|
+
def text(self, value: str):
|
|
235
|
+
self._text = value
|
|
236
|
+
if self._ptr:
|
|
237
|
+
lib = _get_lib()
|
|
238
|
+
lib.baba_label_set_text(self._ptr, value.encode("utf-8"))
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def color(self) -> Color:
|
|
242
|
+
return self._color
|
|
243
|
+
|
|
244
|
+
@color.setter
|
|
245
|
+
def color(self, value: Color):
|
|
246
|
+
self._color = value
|
|
247
|
+
if self._ptr:
|
|
248
|
+
lib = _get_lib()
|
|
249
|
+
lib.baba_label_set_color(self._ptr, value.to_c())
|
|
250
|
+
|
|
251
|
+
@property
|
|
252
|
+
def font_size(self) -> float:
|
|
253
|
+
return self._font_size
|
|
254
|
+
|
|
255
|
+
@font_size.setter
|
|
256
|
+
def font_size(self, value: float):
|
|
257
|
+
self._font_size = value
|
|
258
|
+
if self._ptr:
|
|
259
|
+
lib = _get_lib()
|
|
260
|
+
lib.baba_label_set_font_size(self._ptr, value)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class TextBox(Widget):
|
|
264
|
+
def __init__(self, placeholder: str = ""):
|
|
265
|
+
super().__init__()
|
|
266
|
+
lib = _get_lib()
|
|
267
|
+
self._ptr = lib.baba_textbox_create(placeholder.encode("utf-8"))
|
|
268
|
+
self._placeholder = placeholder
|
|
269
|
+
self._password_mode = False
|
|
270
|
+
|
|
271
|
+
@property
|
|
272
|
+
def text(self) -> str:
|
|
273
|
+
if not self._ptr:
|
|
274
|
+
return ""
|
|
275
|
+
lib = _get_lib()
|
|
276
|
+
c_text = lib.baba_textbox_get_text(self._ptr)
|
|
277
|
+
return ffi.string(c_text).decode("utf-8")
|
|
278
|
+
|
|
279
|
+
@text.setter
|
|
280
|
+
def text(self, value: str):
|
|
281
|
+
if self._ptr:
|
|
282
|
+
lib = _get_lib()
|
|
283
|
+
lib.baba_textbox_set_text(self._ptr, value.encode("utf-8"))
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def password_mode(self) -> bool:
|
|
287
|
+
return self._password_mode
|
|
288
|
+
|
|
289
|
+
@password_mode.setter
|
|
290
|
+
def password_mode(self, value: bool):
|
|
291
|
+
self._password_mode = value
|
|
292
|
+
if self._ptr:
|
|
293
|
+
lib = _get_lib()
|
|
294
|
+
lib.baba_textbox_set_password_mode(self._ptr, value)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class Window:
|
|
298
|
+
def __init__(self, app: App, title: str, width: int = 800, height: int = 600):
|
|
299
|
+
self._app = app
|
|
300
|
+
lib = _get_lib()
|
|
301
|
+
self._ptr = lib.baba_window_create(app._ptr, title.encode("utf-8"), width, height)
|
|
302
|
+
self._title = title
|
|
303
|
+
self._root: Optional[Widget] = None
|
|
304
|
+
|
|
305
|
+
@property
|
|
306
|
+
def title(self) -> str:
|
|
307
|
+
return self._title
|
|
308
|
+
|
|
309
|
+
@title.setter
|
|
310
|
+
def title(self, value: str):
|
|
311
|
+
self._title = value
|
|
312
|
+
if self._ptr:
|
|
313
|
+
lib = _get_lib()
|
|
314
|
+
lib.baba_window_set_title(self._ptr, value.encode("utf-8"))
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def size(self) -> Tuple[int, int]:
|
|
318
|
+
if not self._ptr:
|
|
319
|
+
return (0, 0)
|
|
320
|
+
lib = _get_lib()
|
|
321
|
+
w = ffi.new("int*")
|
|
322
|
+
h = ffi.new("int*")
|
|
323
|
+
lib.baba_window_get_size(self._ptr, w, h)
|
|
324
|
+
return (w[0], h[0])
|
|
325
|
+
|
|
326
|
+
@size.setter
|
|
327
|
+
def size(self, value: Tuple[int, int]):
|
|
328
|
+
if self._ptr:
|
|
329
|
+
lib = _get_lib()
|
|
330
|
+
lib.baba_window_set_size(self._ptr, value[0], value[1])
|
|
331
|
+
|
|
332
|
+
@property
|
|
333
|
+
def root(self) -> Widget:
|
|
334
|
+
if self._root is None:
|
|
335
|
+
lib = _get_lib()
|
|
336
|
+
ptr = lib.baba_window_get_root(self._ptr)
|
|
337
|
+
self._root = Widget(ptr)
|
|
338
|
+
return self._root
|
|
339
|
+
|
|
340
|
+
def show(self):
|
|
341
|
+
if self._ptr:
|
|
342
|
+
lib = _get_lib()
|
|
343
|
+
lib.baba_window_show(self._ptr)
|
|
344
|
+
|
|
345
|
+
def hide(self):
|
|
346
|
+
if self._ptr:
|
|
347
|
+
lib = _get_lib()
|
|
348
|
+
lib.baba_window_hide(self._ptr)
|
|
349
|
+
|
|
350
|
+
def close(self):
|
|
351
|
+
if self._ptr:
|
|
352
|
+
lib = _get_lib()
|
|
353
|
+
lib.baba_window_close(self._ptr)
|
|
354
|
+
|
|
355
|
+
def destroy(self):
|
|
356
|
+
if self._ptr:
|
|
357
|
+
lib = _get_lib()
|
|
358
|
+
lib.baba_window_destroy(self._ptr)
|
|
359
|
+
self._ptr = None
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
class App:
|
|
363
|
+
def __init__(self):
|
|
364
|
+
lib = _get_lib()
|
|
365
|
+
self._ptr = lib.baba_app_create()
|
|
366
|
+
self._windows: List[Window] = []
|
|
367
|
+
self._running = False
|
|
368
|
+
|
|
369
|
+
def create_window(self, title: str, width: int = 800, height: int = 600) -> Window:
|
|
370
|
+
window = Window(self, title, width, height)
|
|
371
|
+
self._windows.append(window)
|
|
372
|
+
return window
|
|
373
|
+
|
|
374
|
+
def run(self) -> int:
|
|
375
|
+
if not self._ptr:
|
|
376
|
+
return -1
|
|
377
|
+
lib = _get_lib()
|
|
378
|
+
self._running = True
|
|
379
|
+
return lib.baba_app_run(self._ptr)
|
|
380
|
+
|
|
381
|
+
def quit(self):
|
|
382
|
+
if self._ptr:
|
|
383
|
+
lib = _get_lib()
|
|
384
|
+
lib.baba_app_quit(self._ptr)
|
|
385
|
+
self._running = False
|
|
386
|
+
|
|
387
|
+
def destroy(self):
|
|
388
|
+
if self._ptr:
|
|
389
|
+
lib = _get_lib()
|
|
390
|
+
for window in self._windows:
|
|
391
|
+
window.destroy()
|
|
392
|
+
self._windows.clear()
|
|
393
|
+
lib.baba_app_destroy(self._ptr)
|
|
394
|
+
self._ptr = None
|
|
395
|
+
|
|
396
|
+
def __enter__(self):
|
|
397
|
+
return self
|
|
398
|
+
|
|
399
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
400
|
+
self.destroy()
|
|
401
|
+
return False
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
@contextmanager
|
|
405
|
+
def create_app():
|
|
406
|
+
app = App()
|
|
407
|
+
try:
|
|
408
|
+
yield app
|
|
409
|
+
finally:
|
|
410
|
+
app.destroy()
|
baba/_ffi/ffi_build.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from cffi import FFI
|
|
2
|
+
|
|
3
|
+
ffi = FFI()
|
|
4
|
+
|
|
5
|
+
ffi.cdef("""
|
|
6
|
+
typedef struct BabaApp BabaApp;
|
|
7
|
+
typedef struct BabaWindow BabaWindow;
|
|
8
|
+
typedef struct BabaWidget BabaWidget;
|
|
9
|
+
typedef struct BabaContext BabaContext;
|
|
10
|
+
|
|
11
|
+
typedef enum {
|
|
12
|
+
BABA_SUCCESS = 0,
|
|
13
|
+
BABA_ERROR_UNKNOWN = -1,
|
|
14
|
+
BABA_ERROR_INVALID_PARAM = -2,
|
|
15
|
+
BABA_ERROR_OUT_OF_MEMORY = -3,
|
|
16
|
+
BABA_ERROR_VULKAN = -4,
|
|
17
|
+
BABA_ERROR_PLATFORM = -5,
|
|
18
|
+
BABA_ERROR_FONT = -6,
|
|
19
|
+
} BabaResult;
|
|
20
|
+
|
|
21
|
+
typedef struct {
|
|
22
|
+
float x, y;
|
|
23
|
+
} BabaVec2;
|
|
24
|
+
|
|
25
|
+
typedef struct {
|
|
26
|
+
float x, y, width, height;
|
|
27
|
+
} BabaRect;
|
|
28
|
+
|
|
29
|
+
typedef struct {
|
|
30
|
+
float r, g, b, a;
|
|
31
|
+
} BabaColor;
|
|
32
|
+
|
|
33
|
+
BabaApp* baba_app_create(void);
|
|
34
|
+
void baba_app_destroy(BabaApp* app);
|
|
35
|
+
int baba_app_run(BabaApp* app);
|
|
36
|
+
void baba_app_quit(BabaApp* app);
|
|
37
|
+
|
|
38
|
+
BabaWindow* baba_window_create(BabaApp* app, const char* title, int width, int height);
|
|
39
|
+
void baba_window_destroy(BabaWindow* window);
|
|
40
|
+
void baba_window_set_title(BabaWindow* window, const char* title);
|
|
41
|
+
void baba_window_set_size(BabaWindow* window, int width, int height);
|
|
42
|
+
void baba_window_get_size(BabaWindow* window, int* width, int* height);
|
|
43
|
+
void baba_window_show(BabaWindow* window);
|
|
44
|
+
void baba_window_hide(BabaWindow* window);
|
|
45
|
+
void baba_window_close(BabaWindow* window);
|
|
46
|
+
BabaWidget* baba_window_get_root(BabaWindow* window);
|
|
47
|
+
|
|
48
|
+
BabaWidget* baba_widget_create(void* vtable, size_t extra_size);
|
|
49
|
+
void baba_widget_destroy(BabaWidget* widget);
|
|
50
|
+
void baba_widget_add_child(BabaWidget* parent, BabaWidget* child);
|
|
51
|
+
void baba_widget_remove_child(BabaWidget* parent, BabaWidget* child);
|
|
52
|
+
BabaWidget* baba_widget_get_child(BabaWidget* widget, size_t index);
|
|
53
|
+
size_t baba_widget_get_child_count(BabaWidget* widget);
|
|
54
|
+
void baba_widget_set_bounds(BabaWidget* widget, BabaRect bounds);
|
|
55
|
+
BabaRect baba_widget_get_bounds(BabaWidget* widget);
|
|
56
|
+
void baba_widget_set_visible(BabaWidget* widget, bool visible);
|
|
57
|
+
bool baba_widget_is_visible(BabaWidget* widget);
|
|
58
|
+
void baba_widget_invalidate(BabaWidget* widget);
|
|
59
|
+
|
|
60
|
+
BabaWidget* baba_button_create(const char* text);
|
|
61
|
+
void baba_button_set_text(BabaWidget* button, const char* text);
|
|
62
|
+
const char* baba_button_get_text(BabaWidget* button);
|
|
63
|
+
void baba_button_set_on_click(BabaWidget* button, void* callback, void* user_data);
|
|
64
|
+
|
|
65
|
+
BabaWidget* baba_label_create(const char* text);
|
|
66
|
+
void baba_label_set_text(BabaWidget* label, const char* text);
|
|
67
|
+
const char* baba_label_get_text(BabaWidget* label);
|
|
68
|
+
void baba_label_set_color(BabaWidget* label, BabaColor color);
|
|
69
|
+
void baba_label_set_font_size(BabaWidget* label, float size);
|
|
70
|
+
|
|
71
|
+
BabaWidget* baba_textbox_create(const char* placeholder);
|
|
72
|
+
void baba_textbox_set_text(BabaWidget* textbox, const char* text);
|
|
73
|
+
const char* baba_textbox_get_text(BabaWidget* textbox);
|
|
74
|
+
void baba_textbox_set_password_mode(BabaWidget* textbox, bool password);
|
|
75
|
+
|
|
76
|
+
const char* baba_get_error_string(int result);
|
|
77
|
+
|
|
78
|
+
typedef void (*BabaButtonClickCallback)(BabaWidget* button, void* user_data);
|
|
79
|
+
""")
|
|
80
|
+
|
|
81
|
+
__all__ = ["ffi", "load_baba"]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def load_baba():
|
|
85
|
+
import os
|
|
86
|
+
import sys
|
|
87
|
+
import ctypes.util
|
|
88
|
+
from pathlib import Path
|
|
89
|
+
|
|
90
|
+
script_dir = Path(__file__).parent
|
|
91
|
+
|
|
92
|
+
lib_paths = []
|
|
93
|
+
|
|
94
|
+
if sys.platform == "darwin":
|
|
95
|
+
lib_names = ["libbaba.dylib", "libbaba_macos.dylib"]
|
|
96
|
+
for name in lib_names:
|
|
97
|
+
lib_paths.extend([
|
|
98
|
+
script_dir / "lib" / name,
|
|
99
|
+
script_dir / name,
|
|
100
|
+
script_dir.parent / "build" / name,
|
|
101
|
+
])
|
|
102
|
+
elif sys.platform == "win32":
|
|
103
|
+
lib_names = ["baba.dll", "libbaba_windows.dll"]
|
|
104
|
+
for name in lib_names:
|
|
105
|
+
lib_paths.extend([
|
|
106
|
+
script_dir / "lib" / name,
|
|
107
|
+
script_dir / name,
|
|
108
|
+
script_dir.parent / "build" / name,
|
|
109
|
+
script_dir.parent / "build" / "Release" / name,
|
|
110
|
+
])
|
|
111
|
+
else:
|
|
112
|
+
lib_names = ["libbaba.so", "libbaba_linux.so"]
|
|
113
|
+
for name in lib_names:
|
|
114
|
+
lib_paths.extend([
|
|
115
|
+
script_dir / "lib" / name,
|
|
116
|
+
script_dir / name,
|
|
117
|
+
script_dir.parent / "build" / name,
|
|
118
|
+
])
|
|
119
|
+
|
|
120
|
+
if hasattr(sys, "prefix"):
|
|
121
|
+
prefix_lib = Path(sys.prefix) / "lib"
|
|
122
|
+
for name in lib_names if 'lib_names' in dir() else ["libbaba"]:
|
|
123
|
+
lib_paths.insert(0, prefix_lib / name)
|
|
124
|
+
|
|
125
|
+
for path in lib_paths:
|
|
126
|
+
if path.exists():
|
|
127
|
+
return ffi.dlopen(str(path))
|
|
128
|
+
|
|
129
|
+
system_lib = ctypes.util.find_library("baba")
|
|
130
|
+
if system_lib:
|
|
131
|
+
return ffi.dlopen(system_lib)
|
|
132
|
+
|
|
133
|
+
searched = "\n".join(f" - {p}" for p in lib_paths)
|
|
134
|
+
raise RuntimeError(
|
|
135
|
+
f"Could not find Baba library.\n"
|
|
136
|
+
f"Searched paths:\n{searched}\n\n"
|
|
137
|
+
f"To use Baba GUI, you need to either:\n"
|
|
138
|
+
f"1. Install from PyPI: pip install baba-gui\n"
|
|
139
|
+
f"2. Build from source with Vulkan SDK installed\n"
|
|
140
|
+
)
|
baba/include/baba.h
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#ifndef BABA_H
|
|
2
|
+
#define BABA_H
|
|
3
|
+
|
|
4
|
+
#include "core/types.h"
|
|
5
|
+
|
|
6
|
+
#define BABA_VERSION_MAJOR 0
|
|
7
|
+
#define BABA_VERSION_MINOR 1
|
|
8
|
+
#define BABA_VERSION_PATCH 0
|
|
9
|
+
|
|
10
|
+
typedef struct BabaApp BabaApp;
|
|
11
|
+
typedef struct BabaWindow BabaWindow;
|
|
12
|
+
typedef struct BabaWidget BabaWidget;
|
|
13
|
+
typedef struct BabaContext BabaContext;
|
|
14
|
+
|
|
15
|
+
typedef struct {
|
|
16
|
+
float x, y;
|
|
17
|
+
} BabaVec2;
|
|
18
|
+
|
|
19
|
+
typedef struct {
|
|
20
|
+
float x, y, width, height;
|
|
21
|
+
} BabaRect;
|
|
22
|
+
|
|
23
|
+
typedef struct {
|
|
24
|
+
float r, g, b, a;
|
|
25
|
+
} BabaColor;
|
|
26
|
+
|
|
27
|
+
typedef struct {
|
|
28
|
+
int (*on_create)(BabaWidget* widget, void* user_data);
|
|
29
|
+
int (*on_destroy)(BabaWidget* widget, void* user_data);
|
|
30
|
+
int (*on_draw)(BabaWidget* widget, BabaContext* ctx, void* user_data);
|
|
31
|
+
int (*on_event)(BabaWidget* widget, void* event, void* user_data);
|
|
32
|
+
void* user_data;
|
|
33
|
+
} BabaWidgetCallbacks;
|
|
34
|
+
|
|
35
|
+
BabaApp* baba_app_create(void);
|
|
36
|
+
void baba_app_destroy(BabaApp* app);
|
|
37
|
+
int baba_app_run(BabaApp* app);
|
|
38
|
+
void baba_app_quit(BabaApp* app);
|
|
39
|
+
|
|
40
|
+
BabaWindow* baba_window_create(BabaApp* app, const char* title, int width, int height);
|
|
41
|
+
void baba_window_destroy(BabaWindow* window);
|
|
42
|
+
void baba_window_set_title(BabaWindow* window, const char* title);
|
|
43
|
+
void baba_window_set_size(BabaWindow* window, int width, int height);
|
|
44
|
+
void baba_window_get_size(BabaWindow* window, int* width, int* height);
|
|
45
|
+
void baba_window_set_position(BabaWindow* window, int x, int y);
|
|
46
|
+
void baba_window_get_position(BabaWindow* window, int* x, int* y);
|
|
47
|
+
void baba_window_show(BabaWindow* window);
|
|
48
|
+
void baba_window_hide(BabaWindow* window);
|
|
49
|
+
void baba_window_close(BabaWindow* window);
|
|
50
|
+
BabaWidget* baba_window_get_root(BabaWindow* window);
|
|
51
|
+
|
|
52
|
+
const char* baba_get_error_string(BabaResult result);
|
|
53
|
+
|
|
54
|
+
#endif
|