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