hspylib-clitt 0.9.41__py3-none-any.whl → 0.9.51__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.
Potentially problematic release.
This version of hspylib-clitt might be problematic. Click here for more details.
- clitt/.version +1 -1
- clitt/__classpath__.py +1 -1
- clitt/__init__.py +4 -3
- clitt/__main__.py +8 -41
- clitt/addons/__init__.py +2 -3
- clitt/addons/appman/__init__.py +2 -2
- clitt/addons/appman/appman.py +1 -1
- clitt/addons/appman/templates/__init__.py +2 -2
- clitt/addons/widman/__init__.py +2 -2
- clitt/addons/widman/widgets/__init__.py +2 -2
- clitt/addons/widman/widgets/widget_free.py +2 -1
- clitt/addons/widman/widgets/widget_punch.py +3 -2
- clitt/core/__init__.py +3 -3
- clitt/core/icons/__init__.py +2 -2
- clitt/core/icons/emojis/__init__.py +2 -2
- clitt/core/icons/font_awesome/__init__.py +2 -2
- clitt/core/term/__init__.py +14 -0
- clitt/core/term/commons.py +63 -0
- clitt/core/term/cursor.py +124 -0
- clitt/core/term/screen.py +96 -0
- clitt/core/term/terminal.py +173 -0
- clitt/core/tui/__init__.py +3 -4
- clitt/core/tui/mchoose/__init__.py +2 -2
- clitt/core/tui/mchoose/menu_choose.py +6 -5
- clitt/core/tui/mdashboard/__init__.py +2 -2
- clitt/core/tui/mdashboard/menu_dashboard.py +9 -9
- clitt/core/tui/menu/__init__.py +2 -2
- clitt/core/tui/menu/tui_menu_action.py +1 -3
- clitt/core/tui/menu/tui_menu_item.py +6 -5
- clitt/core/tui/menu/tui_menu_ui.py +5 -7
- clitt/core/tui/menu/tui_menu_view.py +2 -3
- clitt/core/tui/minput/__init__.py +2 -2
- clitt/core/tui/minput/menu_input.py +22 -18
- clitt/core/tui/minput/minput_utils.py +4 -3
- clitt/core/tui/mselect/__init__.py +2 -2
- clitt/core/tui/mselect/menu_select.py +6 -5
- clitt/core/tui/table/__init__.py +2 -2
- clitt/core/tui/table/table_renderer.py +13 -15
- clitt/core/tui/tui_application.py +27 -13
- clitt/core/tui/tui_component.py +28 -21
- clitt/core/tui/tui_preferences.py +3 -0
- clitt/utils/__init__.py +11 -0
- clitt/utils/git_utils.py +67 -0
- clitt/welcome.txt +6 -6
- {hspylib_clitt-0.9.41.dist-info → hspylib_clitt-0.9.51.dist-info}/METADATA +4 -4
- hspylib_clitt-0.9.51.dist-info/RECORD +90 -0
- clitt/addons/setman/__init__.py +0 -13
- clitt/addons/setman/setman.py +0 -148
- clitt/addons/setman/setman_config.py +0 -26
- clitt/addons/setman/setman_enums.py +0 -54
- clitt/core/settings/__init__.py +0 -15
- clitt/core/settings/settings.py +0 -182
- clitt/core/settings/settings_config.py +0 -47
- clitt/core/settings/settings_entry.py +0 -108
- clitt/core/settings/settings_repository.py +0 -103
- clitt/core/settings/settings_service.py +0 -50
- clitt/core/tui/tui_screen.py +0 -222
- hspylib_clitt-0.9.41.dist-info/RECORD +0 -94
- {hspylib_clitt-0.9.41.dist-info → hspylib_clitt-0.9.51.dist-info}/WHEEL +0 -0
- {hspylib_clitt-0.9.41.dist-info → hspylib_clitt-0.9.51.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
@project: HsPyLib
|
|
6
|
+
@package: hspylib.modules.cli.vt100
|
|
7
|
+
@file: terminal.py
|
|
8
|
+
@created: Tue, 11 May 2021
|
|
9
|
+
@author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior"
|
|
10
|
+
@site: https://github.com/yorevs/hspylib
|
|
11
|
+
@license: MIT - Please refer to <https://opensource.org/licenses/MIT>
|
|
12
|
+
|
|
13
|
+
Copyright 2023, HsPyLib team
|
|
14
|
+
"""
|
|
15
|
+
import atexit
|
|
16
|
+
import logging as log
|
|
17
|
+
import os
|
|
18
|
+
import platform
|
|
19
|
+
import select
|
|
20
|
+
import shlex
|
|
21
|
+
import signal
|
|
22
|
+
import subprocess
|
|
23
|
+
import sys
|
|
24
|
+
from typing import Optional, Tuple, Any
|
|
25
|
+
|
|
26
|
+
from hspylib.core.enums.charset import Charset
|
|
27
|
+
from hspylib.core.exception.exceptions import NotATerminalError
|
|
28
|
+
from hspylib.core.metaclass.singleton import Singleton
|
|
29
|
+
from hspylib.core.tools.commons import sysout
|
|
30
|
+
from hspylib.modules.application.exit_status import ExitStatus
|
|
31
|
+
from hspylib.modules.cli.keyboard import Keyboard
|
|
32
|
+
from hspylib.modules.cli.vt100.vt_100 import Vt100
|
|
33
|
+
|
|
34
|
+
from clitt.core.term.screen import Screen
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Terminal(metaclass=Singleton):
|
|
38
|
+
"""Utility class to provide terminal features."""
|
|
39
|
+
|
|
40
|
+
INSTANCE = None
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def is_a_tty() -> bool:
|
|
44
|
+
return sys.stdout.isatty()
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def shell_exec(cmd_line: str, **kwargs) -> Tuple[Optional[str], ExitStatus]:
|
|
48
|
+
"""Execute command with arguments and return it's run status."""
|
|
49
|
+
try:
|
|
50
|
+
if "stdout" in kwargs:
|
|
51
|
+
del kwargs["stdout"] # Deleted since we use our own stream
|
|
52
|
+
if "stderr" in kwargs:
|
|
53
|
+
del kwargs["stderr"] # Deleted since we use our own stream
|
|
54
|
+
log.info("Executing shell command: %s", cmd_line)
|
|
55
|
+
cmd_args = list(filter(None, shlex.split(cmd_line)))
|
|
56
|
+
output = subprocess.check_output(cmd_args, **kwargs).decode(Charset.UTF_8.val)
|
|
57
|
+
log.info("Execution result: %s", ExitStatus.SUCCESS)
|
|
58
|
+
return output.strip() if output else None, ExitStatus.SUCCESS
|
|
59
|
+
except subprocess.CalledProcessError as err:
|
|
60
|
+
log.error("Command failed: %s => %s", cmd_line, err)
|
|
61
|
+
return None, ExitStatus.FAILED
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def shell_poll(cmd_line: str, **kwargs) -> None:
|
|
65
|
+
"""Execute command with arguments and continuously poll it's output."""
|
|
66
|
+
if "stdout" in kwargs:
|
|
67
|
+
del kwargs["stdout"] # Deleted since we use our own stream
|
|
68
|
+
if "stderr" in kwargs:
|
|
69
|
+
del kwargs["stderr"] # Deleted since we use our own stream
|
|
70
|
+
try:
|
|
71
|
+
log.info("Polling shell command: %s", cmd_line)
|
|
72
|
+
cmd_args = list(filter(None, shlex.split(cmd_line)))
|
|
73
|
+
with subprocess.Popen(
|
|
74
|
+
cmd_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid, **kwargs) as proc:
|
|
75
|
+
process = select.poll()
|
|
76
|
+
process.register(proc.stdout)
|
|
77
|
+
process.register(proc.stderr)
|
|
78
|
+
while not Keyboard.kbhit():
|
|
79
|
+
if poll_obj := process.poll(0.5):
|
|
80
|
+
line = proc.stdout.readline()
|
|
81
|
+
sysout(line.decode(Charset.UTF_8.val) if isinstance(line, bytes) else line.strip(), end='')
|
|
82
|
+
log.debug("Polling returned: %s", str(poll_obj))
|
|
83
|
+
os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
|
|
84
|
+
except (InterruptedError, KeyboardInterrupt):
|
|
85
|
+
log.warning("Polling process has been interrupted command='%s'", cmd_line)
|
|
86
|
+
except subprocess.CalledProcessError as err:
|
|
87
|
+
log.error("Command failed: %s => %s", cmd_line, err)
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def open(filename: str) -> None:
|
|
91
|
+
"""Open the specified file using the default editor."""
|
|
92
|
+
my_os = os.environ.get("HHS_MY_OS", platform.system())
|
|
93
|
+
if "Darwin" == my_os:
|
|
94
|
+
Terminal.shell_exec(f"open {filename}")
|
|
95
|
+
elif "Linux" == my_os:
|
|
96
|
+
Terminal.shell_exec(f"xdg-open {filename}")
|
|
97
|
+
else:
|
|
98
|
+
raise NotImplementedError(f"OS '{my_os}' is not supported")
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def restore(cls) -> None:
|
|
102
|
+
"""Clear the terminal and restore default attributes [wrap,cursor,echo]."""
|
|
103
|
+
sysout("%MOD(0)%")
|
|
104
|
+
cls.set_attributes(show_cursor=True, auto_wrap=True, enable_echo=True)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def set_enable_echo(cls, enabled: bool = True) -> None:
|
|
108
|
+
"""Enable echo in the terminal.
|
|
109
|
+
:param enabled: whether is enabled or not.
|
|
110
|
+
"""
|
|
111
|
+
if not cls.is_a_tty():
|
|
112
|
+
log.error(NotATerminalError("set_enable_echo:: Requires a terminal (TTY)"))
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
os.popen(f"stty {'echo -raw' if enabled else 'raw -echo min 0'}").read()
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def set_auto_wrap(cls, auto_wrap: bool = True) -> None:
|
|
119
|
+
"""Set auto-wrap mode in the terminal.
|
|
120
|
+
:param auto_wrap: whether auto_wrap is set or not.
|
|
121
|
+
"""
|
|
122
|
+
if not cls.is_a_tty():
|
|
123
|
+
log.warning(NotATerminalError("set_enable_echo:: Requires a terminal (TTY)"))
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
sysout(Vt100.set_auto_wrap(auto_wrap), end="")
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def set_show_cursor(cls, show_cursor: bool = True) -> None:
|
|
130
|
+
"""Show or hide cursor in the terminal.
|
|
131
|
+
:param show_cursor: whether to show or hide he cursor.
|
|
132
|
+
"""
|
|
133
|
+
if not cls.is_a_tty():
|
|
134
|
+
log.warning(NotATerminalError("set_enable_echo:: Requires a terminal (TTY)"))
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
sysout(Vt100.set_show_cursor(show_cursor), end="")
|
|
138
|
+
|
|
139
|
+
@classmethod
|
|
140
|
+
def set_attributes(cls, **attrs) -> None:
|
|
141
|
+
"""Wrapper to set all terminal attributes at once."""
|
|
142
|
+
# fmt: off
|
|
143
|
+
enable_echo = attrs['enable_echo']
|
|
144
|
+
auto_wrap = attrs['auto_wrap']
|
|
145
|
+
show_cursor = attrs['show_cursor']
|
|
146
|
+
# fmt: on
|
|
147
|
+
if enable_echo is not None:
|
|
148
|
+
cls.set_enable_echo(enable_echo)
|
|
149
|
+
if auto_wrap is not None:
|
|
150
|
+
cls.set_auto_wrap(auto_wrap)
|
|
151
|
+
if show_cursor is not None:
|
|
152
|
+
cls.set_show_cursor(show_cursor)
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def clear(cls) -> None:
|
|
156
|
+
"""Clear terminal and move the cursor to HOME position (0, 0)."""
|
|
157
|
+
cls.INSTANCE.screen.clear()
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def echo(cls, obj: Any, end=os.linesep) -> None:
|
|
161
|
+
"""Write the string representation of the object to the screen."""
|
|
162
|
+
cls.INSTANCE.screen.cursor.write(obj, end=end)
|
|
163
|
+
|
|
164
|
+
def __init__(self):
|
|
165
|
+
self._screen = Screen.INSTANCE or Screen()
|
|
166
|
+
atexit.register(self.restore)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def screen(self) -> Screen:
|
|
170
|
+
return self._screen
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
assert Terminal().INSTANCE is not None, "Failed to create Terminal instance"
|
clitt/core/tui/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui
|
|
6
6
|
"""Package initialization."""
|
|
@@ -14,7 +14,6 @@ __all__ = [
|
|
|
14
14
|
'table',
|
|
15
15
|
'tui_application',
|
|
16
16
|
'tui_component',
|
|
17
|
-
'tui_preferences'
|
|
18
|
-
'tui_screen'
|
|
17
|
+
'tui_preferences'
|
|
19
18
|
]
|
|
20
|
-
__version__ = '0.9.
|
|
19
|
+
__version__ = '0.9.51'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.mchoose
|
|
6
6
|
"""Package initialization."""
|
|
@@ -9,4 +9,4 @@ __all__ = [
|
|
|
9
9
|
'mchoose',
|
|
10
10
|
'menu_choose'
|
|
11
11
|
]
|
|
12
|
-
__version__ = '0.9.
|
|
12
|
+
__version__ = '0.9.51'
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
15
|
from clitt.core.icons.font_awesome.nav_icons import NavIcons
|
|
16
|
+
from clitt.core.term.cursor import Cursor
|
|
17
|
+
from clitt.core.term.screen import Screen
|
|
16
18
|
from clitt.core.tui.tui_component import TUIComponent
|
|
17
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
18
19
|
from functools import cached_property
|
|
19
20
|
from hspylib.modules.cli.keyboard import Keyboard
|
|
20
21
|
from typing import List, Optional, TypeVar
|
|
@@ -66,7 +67,7 @@ class MenuChoose(TUIComponent):
|
|
|
66
67
|
break # When the index is greater than the number of items, stop rendering
|
|
67
68
|
|
|
68
69
|
option_line = str(self.items[idx])
|
|
69
|
-
self.cursor.erase(
|
|
70
|
+
self.cursor.erase(Screen.Portion.LINE)
|
|
70
71
|
# Print the selector if the index is currently selected
|
|
71
72
|
selector = self.draw_selector(idx == self.sel_index)
|
|
72
73
|
mark = self.prefs.marked if self.sel_options[idx] == 1 else self.prefs.unmarked
|
|
@@ -127,8 +128,8 @@ class MenuChoose(TUIComponent):
|
|
|
127
128
|
self.write(f"{keystroke.value if keystroke else ''}")
|
|
128
129
|
index_len += 1
|
|
129
130
|
# Erase the index typed by the user
|
|
130
|
-
self.cursor.move(index_len,
|
|
131
|
-
self.cursor.erase(
|
|
131
|
+
self.cursor.move(index_len, Cursor.Direction.LEFT)
|
|
132
|
+
self.cursor.erase(Cursor.Direction.RIGHT)
|
|
132
133
|
if typed_index and 1 <= int(typed_index) <= length:
|
|
133
134
|
self.show_to = max(int(typed_index), self.diff_index)
|
|
134
135
|
self.show_from = self.show_to - self.diff_index
|
|
@@ -185,5 +186,5 @@ class MenuChoose(TUIComponent):
|
|
|
185
186
|
self._re_render = True
|
|
186
187
|
|
|
187
188
|
def _max_rows(self) -> int:
|
|
188
|
-
max_rows = self.screen.
|
|
189
|
+
max_rows = self.screen.lines - self.ROW_OFFSET
|
|
189
190
|
return max_rows if self.prefs.max_rows > max_rows else self.prefs.max_rows
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.mdashboard
|
|
6
6
|
"""Package initialization."""
|
|
@@ -11,4 +11,4 @@ __all__ = [
|
|
|
11
11
|
'mdashboard',
|
|
12
12
|
'menu_dashboard'
|
|
13
13
|
]
|
|
14
|
-
__version__ = '0.9.
|
|
14
|
+
__version__ = '0.9.51'
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
15
|
from clitt.core.icons.font_awesome.nav_icons import NavIcons
|
|
16
|
+
from clitt.core.term.cursor import Cursor
|
|
17
|
+
from clitt.core.term.screen import Screen
|
|
16
18
|
from clitt.core.tui.mdashboard.dashboard_builder import DashboardBuilder
|
|
17
19
|
from clitt.core.tui.mdashboard.dashboard_item import DashboardItem
|
|
18
20
|
from clitt.core.tui.tui_component import TUIComponent
|
|
19
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
20
21
|
from hspylib.core.preconditions import check_state
|
|
21
22
|
from hspylib.modules.cli.keyboard import Keyboard
|
|
22
23
|
from typing import List, Optional, TypeVar
|
|
@@ -82,7 +83,7 @@ class MenuDashBoard(TUIComponent):
|
|
|
82
83
|
idx, item, MenuDashBoard.CELL_TPL if self._tab_index != idx else MenuDashBoard.SEL_CELL_TPL
|
|
83
84
|
)
|
|
84
85
|
|
|
85
|
-
self.cursor.erase(
|
|
86
|
+
self.cursor.erase(Screen.Portion.LINE)
|
|
86
87
|
self.cursor.move_to(column=1)
|
|
87
88
|
self.draw_navbar(self.navbar())
|
|
88
89
|
self._re_render = False
|
|
@@ -119,7 +120,6 @@ class MenuDashBoard(TUIComponent):
|
|
|
119
120
|
:param item: the dashboard item.
|
|
120
121
|
:param cell_template: the template of the dashboard cell (selected or unselected).
|
|
121
122
|
"""
|
|
122
|
-
|
|
123
123
|
num_cols, num_rows = len(cell_template[0]), len(cell_template)
|
|
124
124
|
for row in range(0, num_rows):
|
|
125
125
|
for col in range(0, num_cols):
|
|
@@ -127,13 +127,13 @@ class MenuDashBoard(TUIComponent):
|
|
|
127
127
|
# Print current cell
|
|
128
128
|
self.write(f"{item.icon if cur_cell == self.ICN else cur_cell}")
|
|
129
129
|
# Move to the next the next row
|
|
130
|
-
self.cursor.move(1,
|
|
131
|
-
self.cursor.move(num_cols,
|
|
130
|
+
self.cursor.move(1, Cursor.Direction.DOWN)
|
|
131
|
+
self.cursor.move(num_cols, Cursor.Direction.LEFT)
|
|
132
132
|
if item_idx > 0 and (item_idx + 1) % self.prefs.items_per_line == 0:
|
|
133
133
|
# Break the line
|
|
134
|
-
self.cursor.move(1,
|
|
135
|
-
self.cursor.move(num_cols * self.prefs.items_per_line,
|
|
134
|
+
self.cursor.move(1, Cursor.Direction.DOWN)
|
|
135
|
+
self.cursor.move(num_cols * self.prefs.items_per_line, Cursor.Direction.LEFT)
|
|
136
136
|
elif item_idx + 1 < len(self._items):
|
|
137
137
|
# Continue on the same line
|
|
138
|
-
self.cursor.move(num_rows,
|
|
139
|
-
self.cursor.move(num_cols,
|
|
138
|
+
self.cursor.move(num_rows, Cursor.Direction.UP)
|
|
139
|
+
self.cursor.move(num_cols, Cursor.Direction.RIGHT)
|
clitt/core/tui/menu/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.menu
|
|
6
6
|
"""Package initialization."""
|
|
@@ -13,4 +13,4 @@ __all__ = [
|
|
|
13
13
|
'tui_menu_ui',
|
|
14
14
|
'tui_menu_view'
|
|
15
15
|
]
|
|
16
|
-
__version__ = '0.9.
|
|
16
|
+
__version__ = '0.9.51'
|
|
@@ -12,10 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
|
-
|
|
16
15
|
from clitt.core.tui.menu.tui_menu import ON_TRIGGER_CB, TUIMenu
|
|
17
16
|
from clitt.core.tui.menu.tui_menu_ui import TUIMenuUi
|
|
18
|
-
from hspylib.modules.cli.vt100.vt_utils import restore_cursor
|
|
19
17
|
from typing import Optional
|
|
20
18
|
|
|
21
19
|
|
|
@@ -30,7 +28,7 @@ class TUIMenuAction(TUIMenu):
|
|
|
30
28
|
return ret_menu if ret_menu else self._default_trigger_cb(self)
|
|
31
29
|
|
|
32
30
|
def render(self) -> None:
|
|
33
|
-
|
|
31
|
+
self.screen.cursor.restore()
|
|
34
32
|
TUIMenuUi.render_app_title()
|
|
35
33
|
|
|
36
34
|
def __init__(
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from clitt.core.icons.font_awesome.nav_icons import NavIcons
|
|
17
|
+
from clitt.core.term.cursor import Cursor
|
|
18
|
+
from clitt.core.term.screen import Screen
|
|
17
19
|
from clitt.core.tui.menu.tui_menu import TUIMenu
|
|
18
20
|
from clitt.core.tui.menu.tui_menu_ui import TUIMenuUi
|
|
19
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
20
21
|
from functools import cached_property
|
|
21
22
|
from hspylib.core.tools.dict_tools import get_or_default
|
|
22
23
|
from hspylib.modules.cli.keyboard import Keyboard
|
|
@@ -41,7 +42,7 @@ class TUIMenuItem(TUIMenu):
|
|
|
41
42
|
):
|
|
42
43
|
super().__init__(parent, title, tooltip or f"Access the '{title}' menu")
|
|
43
44
|
self._show_from: int = 0
|
|
44
|
-
self._show_to: int = self.screen.
|
|
45
|
+
self._show_to: int = self.screen.lines - self.ROW_OFFSET
|
|
45
46
|
self._diff_index: int = self._show_to - self._show_from
|
|
46
47
|
self._sel_index: int = 0
|
|
47
48
|
self._items: List[TUIMenu] = items or []
|
|
@@ -81,7 +82,7 @@ class TUIMenuItem(TUIMenu):
|
|
|
81
82
|
if idx >= length:
|
|
82
83
|
break # When the number of items is lower than the max_rows, skip the other lines
|
|
83
84
|
option_line = str(self._items[idx])
|
|
84
|
-
self.cursor.erase(
|
|
85
|
+
self.cursor.erase(Screen.Portion.LINE)
|
|
85
86
|
# Print the selector if the index is currently selected
|
|
86
87
|
selector = self.draw_selector(is_selected=(idx == self._sel_index), has_bg_color=False)
|
|
87
88
|
# fmt: off
|
|
@@ -180,8 +181,8 @@ class TUIMenuItem(TUIMenu):
|
|
|
180
181
|
self.write(f"{keystroke.value if keystroke else ''}")
|
|
181
182
|
index_len += 1
|
|
182
183
|
# Erase the index typed by the user
|
|
183
|
-
self.cursor.move(index_len,
|
|
184
|
-
self.cursor.erase(
|
|
184
|
+
self.cursor.move(index_len, Cursor.Direction.LEFT)
|
|
185
|
+
self.cursor.erase(Cursor.Direction.RIGHT)
|
|
185
186
|
if typed_index and 1 <= int(typed_index) <= length:
|
|
186
187
|
self._show_to = max(int(typed_index), self._diff_index)
|
|
187
188
|
self._show_from = self._show_to - self._diff_index
|
|
@@ -12,13 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
|
-
|
|
15
|
+
from clitt.core.term.terminal import Terminal
|
|
16
16
|
from clitt.core.tui.menu.tui_menu import TUIMenu
|
|
17
17
|
from clitt.core.tui.tui_preferences import TUIPreferences
|
|
18
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
19
18
|
from hspylib.core.metaclass.singleton import Singleton
|
|
20
19
|
from hspylib.core.preconditions import check_not_none
|
|
21
|
-
from hspylib.modules.cli.vt100.vt_utils import set_auto_wrap, set_show_cursor
|
|
22
20
|
|
|
23
21
|
|
|
24
22
|
class TUIMenuUi(metaclass=Singleton):
|
|
@@ -29,9 +27,9 @@ class TUIMenuUi(metaclass=Singleton):
|
|
|
29
27
|
APP_TITLE = "Main Menu"
|
|
30
28
|
|
|
31
29
|
# fmt: off
|
|
32
|
-
PREFS = TUIPreferences.INSTANCE
|
|
30
|
+
PREFS = TUIPreferences.INSTANCE
|
|
33
31
|
|
|
34
|
-
SCREEN =
|
|
32
|
+
SCREEN = Terminal.INSTANCE.screen
|
|
35
33
|
|
|
36
34
|
MENU_LINE = f"{'--' * PREFS.title_line_length}"
|
|
37
35
|
|
|
@@ -89,8 +87,8 @@ class TUIMenuUi(metaclass=Singleton):
|
|
|
89
87
|
|
|
90
88
|
def _prepare_render(self, auto_wrap: bool = True, show_cursor: bool = False, clear_screen: bool = True) -> None:
|
|
91
89
|
"""Prepare the screen for renderization."""
|
|
92
|
-
set_auto_wrap(auto_wrap)
|
|
93
|
-
set_show_cursor(show_cursor)
|
|
90
|
+
Terminal.set_auto_wrap(auto_wrap)
|
|
91
|
+
Terminal.set_show_cursor(show_cursor)
|
|
94
92
|
if clear_screen:
|
|
95
93
|
self.SCREEN.clear()
|
|
96
94
|
self.SCREEN.cursor.save()
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
|
-
|
|
15
|
+
from clitt.core.term.cursor import Cursor
|
|
16
16
|
from clitt.core.tui.menu.tui_menu import ON_TRIGGER_CB, TUIMenu
|
|
17
17
|
from clitt.core.tui.menu.tui_menu_ui import TUIMenuUi
|
|
18
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
19
18
|
from typing import Callable, Optional
|
|
20
19
|
|
|
21
20
|
|
|
@@ -53,6 +52,6 @@ class TUIMenuView(TUIMenu):
|
|
|
53
52
|
self.draw_navbar(self.navbar())
|
|
54
53
|
|
|
55
54
|
def _display_content(self) -> None:
|
|
56
|
-
self.cursor.erase(
|
|
55
|
+
self.cursor.erase(Cursor.Direction.DOWN)
|
|
57
56
|
self.writeln(self._content)
|
|
58
57
|
self.wait_keystroke()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.minput
|
|
6
6
|
"""Package initialization."""
|
|
@@ -16,4 +16,4 @@ __all__ = [
|
|
|
16
16
|
'minput',
|
|
17
17
|
'minput_utils'
|
|
18
18
|
]
|
|
19
|
-
__version__ = '0.9.
|
|
19
|
+
__version__ = '0.9.51'
|
|
@@ -12,23 +12,25 @@
|
|
|
12
12
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
|
+
from functools import reduce
|
|
16
|
+
from time import sleep
|
|
17
|
+
from typing import List
|
|
18
|
+
|
|
19
|
+
import pyperclip
|
|
20
|
+
from hspylib.core.exception.exceptions import InvalidStateError
|
|
21
|
+
from hspylib.core.namespace import Namespace
|
|
22
|
+
from hspylib.modules.cli.keyboard import Keyboard
|
|
23
|
+
|
|
15
24
|
from clitt.core.icons.font_awesome.form_icons import FormIcons
|
|
16
25
|
from clitt.core.icons.font_awesome.nav_icons import NavIcons
|
|
26
|
+
from clitt.core.term.commons import get_cursor_position
|
|
27
|
+
from clitt.core.term.cursor import Cursor
|
|
28
|
+
from clitt.core.term.terminal import Terminal
|
|
17
29
|
from clitt.core.tui.minput.form_builder import FormBuilder
|
|
18
30
|
from clitt.core.tui.minput.form_field import FormField
|
|
19
31
|
from clitt.core.tui.minput.input_type import InputType
|
|
20
32
|
from clitt.core.tui.minput.minput_utils import *
|
|
21
33
|
from clitt.core.tui.tui_component import TUIComponent
|
|
22
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
23
|
-
from functools import reduce
|
|
24
|
-
from hspylib.core.exception.exceptions import InvalidStateError
|
|
25
|
-
from hspylib.core.namespace import Namespace
|
|
26
|
-
from hspylib.modules.cli.keyboard import Keyboard
|
|
27
|
-
from hspylib.modules.cli.vt100.vt_utils import get_cursor_position, set_enable_echo, set_show_cursor
|
|
28
|
-
from time import sleep
|
|
29
|
-
from typing import List
|
|
30
|
-
|
|
31
|
-
import pyperclip
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
class MenuInput(TUIComponent):
|
|
@@ -73,7 +75,7 @@ class MenuInput(TUIComponent):
|
|
|
73
75
|
return None
|
|
74
76
|
|
|
75
77
|
def render(self) -> None:
|
|
76
|
-
set_show_cursor(False)
|
|
78
|
+
Terminal.set_show_cursor(False)
|
|
77
79
|
self.cursor.restore()
|
|
78
80
|
self.writeln(f"{self.prefs.title_color.placeholder}{self.title}%EOL%%NC%")
|
|
79
81
|
|
|
@@ -93,7 +95,7 @@ class MenuInput(TUIComponent):
|
|
|
93
95
|
self.draw_navbar(self.navbar())
|
|
94
96
|
self._re_render = False
|
|
95
97
|
self._set_cursor_pos()
|
|
96
|
-
set_show_cursor()
|
|
98
|
+
Terminal.set_show_cursor()
|
|
97
99
|
|
|
98
100
|
def navbar(self, **kwargs) -> str:
|
|
99
101
|
return (
|
|
@@ -266,7 +268,9 @@ class MenuInput(TUIComponent):
|
|
|
266
268
|
:param field_index: the current form field index.
|
|
267
269
|
:param field_size: the form field length.
|
|
268
270
|
"""
|
|
269
|
-
if f_pos := get_cursor_position()
|
|
271
|
+
if f_pos := get_cursor_position() \
|
|
272
|
+
if self.positions[field_index] == (0, 0) \
|
|
273
|
+
else self.positions[field_index]:
|
|
270
274
|
self.positions[field_index] = f_pos
|
|
271
275
|
if self.tab_index == field_index:
|
|
272
276
|
self.cur_row, self.cur_col = f_pos[0], f_pos[1] + field_size
|
|
@@ -275,17 +279,17 @@ class MenuInput(TUIComponent):
|
|
|
275
279
|
"""Display a form filling or submitting error.
|
|
276
280
|
:param err_msg: the error message.
|
|
277
281
|
"""
|
|
278
|
-
set_enable_echo(False)
|
|
279
|
-
set_show_cursor(False)
|
|
282
|
+
Terminal.set_enable_echo(False)
|
|
283
|
+
Terminal.set_show_cursor(False)
|
|
280
284
|
offset = 15 # Sum of minput used characters that are not related to the field.
|
|
281
285
|
err_pos = offset + self.max_label_length + self.max_value_length + self.max_detail_length
|
|
282
286
|
self.cursor.move_to(self.cur_row, err_pos)
|
|
283
287
|
mi_print_err(self.screen, f"{FormIcons.ARROW_LEFT} {err_msg}")
|
|
284
288
|
# This calculation gives a good delay amount based on the size of the message.
|
|
285
289
|
sleep(max(2.0, int(len(err_msg) / 21)))
|
|
286
|
-
set_enable_echo()
|
|
290
|
+
Terminal.set_enable_echo()
|
|
287
291
|
# Erase the message after the timeout
|
|
288
292
|
self.cursor.move_to(self.cur_row, err_pos)
|
|
289
|
-
self.cursor.erase(
|
|
290
|
-
set_show_cursor()
|
|
293
|
+
self.cursor.erase(Cursor.Direction.RIGHT)
|
|
294
|
+
Terminal.set_show_cursor()
|
|
291
295
|
self._re_render = True
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
16
15
|
from hspylib.core.exception.exceptions import InvalidInputError
|
|
17
16
|
from typing import Any, Optional, Tuple
|
|
18
17
|
|
|
19
18
|
import re
|
|
20
19
|
|
|
20
|
+
from clitt.core.term.screen import Screen
|
|
21
|
+
|
|
21
22
|
MASK_SYMBOLS = ["#", "@", "%", "*"]
|
|
22
23
|
|
|
23
24
|
VALUE_SEPARATORS = r"[|,;]"
|
|
@@ -33,7 +34,7 @@ def detail_len(field: Any) -> int:
|
|
|
33
34
|
return 1 + (2 * max_len)
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def mi_print(screen:
|
|
37
|
+
def mi_print(screen: Screen, text: str, prefix: str = None, field_len: int = 0, end: str = "") -> None:
|
|
37
38
|
"""Special menu input print.
|
|
38
39
|
:param screen: the component's screen.
|
|
39
40
|
:param text: the text to be printed.
|
|
@@ -48,7 +49,7 @@ def mi_print(screen: TUIScreen, text: str, prefix: str = None, field_len: int =
|
|
|
48
49
|
screen.cursor.write(fmt.format(text or ""), end=end)
|
|
49
50
|
|
|
50
51
|
|
|
51
|
-
def mi_print_err(screen:
|
|
52
|
+
def mi_print_err(screen: Screen, text: str) -> None:
|
|
52
53
|
"""Special menu input print error message.
|
|
53
54
|
:param screen: the component's screen.
|
|
54
55
|
:param text: the text to be printed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.mselect
|
|
6
6
|
"""Package initialization."""
|
|
@@ -9,4 +9,4 @@ __all__ = [
|
|
|
9
9
|
'menu_select',
|
|
10
10
|
'mselect'
|
|
11
11
|
]
|
|
12
|
-
__version__ = '0.9.
|
|
12
|
+
__version__ = '0.9.51'
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
Copyright 2023, HsPyLib team
|
|
14
14
|
"""
|
|
15
15
|
from clitt.core.icons.font_awesome.nav_icons import NavIcons
|
|
16
|
+
from clitt.core.term.cursor import Cursor
|
|
17
|
+
from clitt.core.term.screen import Screen
|
|
16
18
|
from clitt.core.tui.tui_component import TUIComponent
|
|
17
|
-
from clitt.core.tui.tui_screen import TUIScreen
|
|
18
19
|
from functools import cached_property
|
|
19
20
|
from hspylib.modules.cli.keyboard import Keyboard
|
|
20
21
|
from typing import List, Optional, TypeVar
|
|
@@ -33,7 +34,7 @@ class MenuSelect(TUIComponent):
|
|
|
33
34
|
super().__init__(title)
|
|
34
35
|
self._items = items
|
|
35
36
|
self._show_from = 0
|
|
36
|
-
self._show_to = self.screen.
|
|
37
|
+
self._show_to = self.screen.lines - self.ROW_OFFSET
|
|
37
38
|
self._diff_index = self._show_to - self._show_from
|
|
38
39
|
self._sel_index = 0
|
|
39
40
|
self._max_line_length = max(len(str(item)) for item in items)
|
|
@@ -67,7 +68,7 @@ class MenuSelect(TUIComponent):
|
|
|
67
68
|
break # When the index is greater than the number of items, stop rendering
|
|
68
69
|
|
|
69
70
|
option_line = str(self._items[idx])
|
|
70
|
-
self.cursor.erase(
|
|
71
|
+
self.cursor.erase(Screen.Portion.LINE)
|
|
71
72
|
# Print the selector if the index is currently selected
|
|
72
73
|
selector = self.draw_selector(idx == self._sel_index)
|
|
73
74
|
# fmt: off
|
|
@@ -122,8 +123,8 @@ class MenuSelect(TUIComponent):
|
|
|
122
123
|
self.cursor.write(f"{keystroke.value if keystroke else ''}")
|
|
123
124
|
index_len += 1
|
|
124
125
|
# Erase the index typed by the user
|
|
125
|
-
self.cursor.move(index_len,
|
|
126
|
-
self.cursor.erase(
|
|
126
|
+
self.cursor.move(index_len, Cursor.Direction.LEFT)
|
|
127
|
+
self.cursor.erase(Cursor.Direction.RIGHT)
|
|
127
128
|
if typed_index and 1 <= int(typed_index) <= length:
|
|
128
129
|
self._show_to = max(int(typed_index), self._diff_index)
|
|
129
130
|
self._show_from = self._show_to - self._diff_index
|
clitt/core/tui/table/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# _*_ coding: utf-8 _*_
|
|
2
2
|
#
|
|
3
|
-
# hspylib-clitt v0.9.
|
|
3
|
+
# hspylib-clitt v0.9.51
|
|
4
4
|
#
|
|
5
5
|
# Package: main.clitt.core.tui.table
|
|
6
6
|
"""Package initialization."""
|
|
@@ -9,4 +9,4 @@ __all__ = [
|
|
|
9
9
|
'table_enums',
|
|
10
10
|
'table_renderer'
|
|
11
11
|
]
|
|
12
|
-
__version__ = '0.9.
|
|
12
|
+
__version__ = '0.9.51'
|