axto 0.0.2__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.
axto-0.0.2/PKG-INFO ADDED
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: axto
3
+ Version: 0.0.2
4
+ Summary: Simple TUI library.
5
+ Author: Rioxpi
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+
9
+ # AXTO
10
+
11
+ 1. Simple TUI library
12
+
13
+ ## INSTALATION
14
+ 1. `pip install axto`
15
+
16
+ ## QUICK START
17
+ ```
18
+ from axto import Engine
19
+ from axto.widgets.box import Box
20
+
21
+ app = Engine()
22
+ box1 = Box(x=5, y=5, width=20, height=10)
23
+ app.add_widget(box1)
24
+ app.run()
25
+ ```
26
+
27
+ ## AVAIABLE WIDGETS
28
+ 1. BOX
29
+
30
+ ## Control
31
+ 1. `q` - exit engine
32
+ 2. `TAB` - select next widget
axto-0.0.2/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # AXTO
2
+
3
+ 1. Simple TUI library
4
+
5
+ ## INSTALATION
6
+ 1. `pip install axto`
7
+
8
+ ## QUICK START
9
+ ```
10
+ from axto import Engine
11
+ from axto.widgets.box import Box
12
+
13
+ app = Engine()
14
+ box1 = Box(x=5, y=5, width=20, height=10)
15
+ app.add_widget(box1)
16
+ app.run()
17
+ ```
18
+
19
+ ## AVAIABLE WIDGETS
20
+ 1. BOX
21
+
22
+ ## Control
23
+ 1. `q` - exit engine
24
+ 2. `TAB` - select next widget
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "axto"
7
+ version = "0.0.2"
8
+ description = "Simple TUI library."
9
+ readme = "README.md"
10
+ requires-python = ">=3.7"
11
+ authors = [
12
+ { name = "Rioxpi" }
13
+ ]
14
+
15
+ [tool.setuptools.packages.find]
16
+ where = ["src"]
axto-0.0.2/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from .core import Engine
2
+
3
+ __version__ = "0.0.2"
@@ -0,0 +1,68 @@
1
+ import sys
2
+ import tty
3
+ import termios
4
+ from .terminal import Terminal
5
+
6
+ class Engine:
7
+ def __init__(self):
8
+ self.running = False
9
+ self.widgets = []
10
+ self._old_settings = None
11
+ self.focus_index = 0 # Index of the currently focused widget
12
+
13
+ def add_widget(self, widget):
14
+ self.widgets.append(widget)
15
+
16
+ def _enable_raw_mode(self):
17
+ """Change terminal to raw mode to read input byte by byte."""
18
+ fd = sys.stdin.fileno()
19
+ self._old_settings = termios.tcgetattr(fd)
20
+ tty.setraw(fd)
21
+
22
+ def _disable_raw_mode(self):
23
+ """Restore standard terminal settings."""
24
+ if self._old_settings:
25
+ termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, self._old_settings)
26
+
27
+ def run(self): # TODO: Hide cursor
28
+ self.running = True
29
+ self._enable_raw_mode()
30
+
31
+ try:
32
+ Terminal.clear_screen()
33
+
34
+ # Focus the first widget if it exists
35
+ if self.widgets:
36
+ self.widgets[self.focus_index].select()
37
+
38
+ while self.running:
39
+ # Draw all widgets
40
+ self._render_all_widgets()
41
+
42
+ # 2. Handle input (reading 1 byte)
43
+ # This blocks the loop until a key is pressed
44
+ key = sys.stdin.read(1)
45
+ self._handle_input(key)
46
+
47
+ if self.widgets:
48
+ active_widget = self.widgets[self.focus_index]
49
+ active_widget.on_key(key)
50
+ finally:
51
+ # Restore terminal settings and clear screen on exit
52
+ self._disable_raw_mode()
53
+ Terminal.clear_screen()
54
+
55
+ def _render_all_widgets(self):
56
+ """Helper method to render all widgets."""
57
+ for widget in self.widgets:
58
+ widget.draw(Terminal)
59
+
60
+ def _handle_input(self, key):
61
+ """Handle user input"""
62
+ if key == 'q':
63
+ self.running = False
64
+ elif key == '\t': # Tab key to switch focus
65
+ self.focus_index = (self.focus_index + 1) % len(self.widgets)
66
+ index_before = (self.focus_index - 1) % len(self.widgets)
67
+ self.widgets[index_before].deselect() # Deselect previous widget
68
+ self.widgets[self.focus_index].select() # Select new widget
@@ -0,0 +1,25 @@
1
+ import sys
2
+
3
+ class Terminal:
4
+ # ANSI escape codes for terminal control
5
+ CLEAR = "\033[2J"
6
+ HOME = "\033[H"
7
+ RESET = "\033[0m"
8
+
9
+ @staticmethod
10
+ def move_cursor(x, y):
11
+ # ANSI uses format: \033[y;xH (first row, then column)
12
+ sys.stdout.write(f"\033[{y};{x}H")
13
+
14
+ @staticmethod
15
+ def clear_screen():
16
+ sys.stdout.write(Terminal.CLEAR + Terminal.HOME)
17
+ sys.stdout.flush()
18
+
19
+ @staticmethod
20
+ def write(text, color_code=None):
21
+ if color_code:
22
+ sys.stdout.write(f"\033[{color_code}m{text}{Terminal.RESET}")
23
+ else:
24
+ sys.stdout.write(text)
25
+ sys.stdout.flush()
@@ -0,0 +1,28 @@
1
+ class Widget:
2
+ def __init__(self, x, y, width, height):
3
+ self.x = x
4
+ self.y = y
5
+ self.width = width
6
+ self.height = height
7
+ self.handlers = {}
8
+ self.selected = False
9
+
10
+ def draw(self, term):
11
+ raise NotImplementedError("Base Widget does not implement draw method")
12
+
13
+ def on_key(self, key):
14
+ """Default key handler, can be overridden by subclasses"""
15
+ if "key" in self.handlers:
16
+ self.handlers["key"](key)
17
+
18
+ def bind(self, event_type, callback):
19
+ """Bind an event handler to a specific event type"""
20
+ self.handlers[event_type] = callback
21
+
22
+ def select(self):
23
+ """Called when the widget is selected/focused"""
24
+ self.selected = True
25
+
26
+ def deselect(self):
27
+ """Called when the widget is deselected/unfocused"""
28
+ self.selected = False
@@ -0,0 +1,35 @@
1
+ from axto.widgets.base import Widget
2
+
3
+ class Box(Widget):
4
+ def __init__(self, x, y, width, height, default_color="37", selected_color="32", border_style="single"):
5
+ super().__init__(x, y, width, height)
6
+ self.default_color = default_color
7
+ self.selected_color = selected_color
8
+ self.border_style = border_style
9
+ if border_style not in ("single", "double", "none"):
10
+ raise ValueError("Unsupported border style. Use 'single', 'double' or 'none'.")
11
+
12
+ def draw(self, term):
13
+ color = self.selected_color if self.selected else self.default_color
14
+
15
+ # Draw top border
16
+ term.move_cursor(self.x, self.y)
17
+ if self.border_style == "single":
18
+ term.write("┌" + "─" * (self.width - 2) + "┐", color)
19
+ elif self.border_style == "double":
20
+ term.write("╔" + "═" * (self.width - 2) + "╗", color)
21
+
22
+ # Draw sides
23
+ for i in range(1, self.height - 1):
24
+ term.move_cursor(self.x, self.y + i)
25
+ if self.border_style == "single":
26
+ term.write("|" + " " * (self.width - 2) + "|", color)
27
+ elif self.border_style == "double":
28
+ term.write("║" + " " * (self.width - 2) + "║", color)
29
+
30
+ # Draw bottom border
31
+ term.move_cursor(self.x, self.y + self.height - 1)
32
+ if self.border_style == "single":
33
+ term.write("└" + "─" * (self.width - 2) + "┘ ", color)
34
+ elif self.border_style == "double":
35
+ term.write("╚" + "═" * (self.width - 2) + "╝ ", color)
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: axto
3
+ Version: 0.0.2
4
+ Summary: Simple TUI library.
5
+ Author: Rioxpi
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+
9
+ # AXTO
10
+
11
+ 1. Simple TUI library
12
+
13
+ ## INSTALATION
14
+ 1. `pip install axto`
15
+
16
+ ## QUICK START
17
+ ```
18
+ from axto import Engine
19
+ from axto.widgets.box import Box
20
+
21
+ app = Engine()
22
+ box1 = Box(x=5, y=5, width=20, height=10)
23
+ app.add_widget(box1)
24
+ app.run()
25
+ ```
26
+
27
+ ## AVAIABLE WIDGETS
28
+ 1. BOX
29
+
30
+ ## Control
31
+ 1. `q` - exit engine
32
+ 2. `TAB` - select next widget
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/axto/__init__.py
4
+ src/axto/core.py
5
+ src/axto/terminal.py
6
+ src/axto.egg-info/PKG-INFO
7
+ src/axto.egg-info/SOURCES.txt
8
+ src/axto.egg-info/dependency_links.txt
9
+ src/axto.egg-info/top_level.txt
10
+ src/axto/widgets/base.py
11
+ src/axto/widgets/box.py
@@ -0,0 +1 @@
1
+ axto