feat(terminal): 输入行跟随输出并显示当前路径
- Text 不再把输入行固定在控件底部:输入行紧跟在最后一条输出之后, 输出增加时随之下移;超出可视区时 scroll_offset 保持输入行可见 - Text 增加 prompt 字段 / set_prompt;点击定位、光标、_exec_input、 滚动条计算都按实际 prompt 长度和总行数(输出行 + 输入行)计算 - Terminal 的提示符改为“归一化路径 + /> ”: 根目录是 "/> ",/apps 是 "/apps/> " - cd 成功后更新提示符;裸 cd 仍打印当前路径
This commit is contained in:
1 parent
ab18afd26e
commit
5e2598a992
2 files changed
+73
-25
No files matched your search
+26
-2
@@ -56,9 +56,9 @@ export def _start() -> int:
|
||||
elif act == 5:
|
||||
aval: int = sbar.action_value
|
||||
if term.line_count > 0:
|
||||
term.scroll_offset = aval * term.line_count / 1000
|
||||
term.scroll_offset = aval * (term.line_count + 1) / 1000
|
||||
vis3: int = term._visible_lines()
|
||||
max_off: int = term.line_count - vis3
|
||||
max_off: int = term.line_count + 1 - vis3
|
||||
if max_off < 0: max_off = 0
|
||||
if term.scroll_offset < 0: term.scroll_offset = 0
|
||||
if term.scroll_offset > max_off: term.scroll_offset = max_off
|
||||
@@ -144,6 +144,28 @@ export def _start() -> int:
|
||||
j += 1
|
||||
*(dst + out_len) = char(0)
|
||||
|
||||
# 提示符 = 归一化路径 + "/> ":根目录显示 "/> ",/apps 显示 "/apps/> "。
|
||||
def _update_prompt():
|
||||
p: [80]char
|
||||
i: int = 0
|
||||
while i < 63:
|
||||
ch: char = _cwd[i]
|
||||
p[i] = ch
|
||||
if ch == 0: break
|
||||
i += 1
|
||||
if i == 0:
|
||||
p[0] = char(47)
|
||||
i = 1
|
||||
if p[i - 1] != char(47):
|
||||
p[i] = char(47)
|
||||
i += 1
|
||||
p[i] = char(62) # '>'
|
||||
i += 1
|
||||
p[i] = char(32) # ' '
|
||||
i += 1
|
||||
p[i] = char(0)
|
||||
term.set_prompt(&p[0])
|
||||
|
||||
def _cmd_cd(path: str):
|
||||
# 裸 cd:只显示当前路径,不切换目录(切换请用 cd <path>)。
|
||||
if path is None or *path == 0:
|
||||
@@ -158,6 +180,7 @@ export def _start() -> int:
|
||||
fs.closedir(dd)
|
||||
# 存入归一化后的路径:根目录的 ".." 保持为 "/",pwd 也保持干净。
|
||||
_normalize_path(&_cwd[0], 64, &full[0])
|
||||
_update_prompt()
|
||||
|
||||
def _cmd_ls():
|
||||
dd: int = fs.opendir(&_cwd[0])
|
||||
@@ -359,6 +382,7 @@ export def _start() -> int:
|
||||
term.insert("Unknown command. Type 'help'.")
|
||||
return 0
|
||||
|
||||
_update_prompt()
|
||||
term.on_command = _on_command
|
||||
|
||||
root.mainloop()
|
||||
|
||||
+47
-23
@@ -1197,6 +1197,7 @@ virtual class Text(Widget):
|
||||
input_buf: [80]char
|
||||
input_len: int
|
||||
cursor_pos: int
|
||||
prompt: [80]char
|
||||
bg_color: uint32
|
||||
text_color: uint32
|
||||
prompt_color: uint32
|
||||
@@ -1223,6 +1224,9 @@ virtual class Text(Widget):
|
||||
self.line_count = 0
|
||||
self.input_len = 0
|
||||
self.cursor_pos = 0
|
||||
self.prompt[0] = '>'
|
||||
self.prompt[1] = ' '
|
||||
self.prompt[2] = 0
|
||||
self.bg_color = uint32(0x0C0C0C)
|
||||
self.text_color = uint32(0xCCCCCC)
|
||||
self.prompt_color = uint32(0x00FF00)
|
||||
@@ -1250,13 +1254,19 @@ virtual class Text(Widget):
|
||||
line_idx: int = self.scroll_offset + i
|
||||
if line_idx < self.line_count:
|
||||
_draw_text(fb, fw, fh, self.x + 4, self.y + i * CHAR_H, &self.lines[line_idx][0], self.text_color)
|
||||
prompt_y: int = self.y + self.h - CHAR_H - 4
|
||||
_draw_text(fb, fw, fh, self.x + 4, prompt_y, "> ", self.prompt_color)
|
||||
if self.input_len > 0:
|
||||
_draw_text(fb, fw, fh, self.x + 4 + 2 * CHAR_W, prompt_y, &self.input_buf[0], uint32(0xFFFFFF))
|
||||
cx_pos: int = self.x + 4 + (2 + self.cursor_pos) * CHAR_W
|
||||
if cx_pos < self.x + self.w - 4:
|
||||
_fill_rect(fb, fw, fh, cx_pos, prompt_y, CHAR_W // 2, CHAR_H, self.cursor_color)
|
||||
# 输入行不是一个固定在底部的控件,而是紧跟在最后一条输出之后的一行;
|
||||
# 输出增多时它随之下移,超出可视区时由 scroll_offset 把它保持在最后一行。
|
||||
prompt_row: int = self.line_count - self.scroll_offset
|
||||
if prompt_row >= 0 and prompt_row < visible:
|
||||
prompt_y: int = self.y + prompt_row * CHAR_H
|
||||
plen: int = int(string.strlen(&self.prompt[0]))
|
||||
_draw_text(fb, fw, fh, self.x + 4, prompt_y, &self.prompt[0], self.prompt_color)
|
||||
if self.input_len > 0:
|
||||
_draw_text(fb, fw, fh, self.x + 4 + plen * CHAR_W, prompt_y,
|
||||
&self.input_buf[0], uint32(0xFFFFFF))
|
||||
cx_pos: int = self.x + 4 + (plen + self.cursor_pos) * CHAR_W
|
||||
if cx_pos < self.x + self.w - 4:
|
||||
_fill_rect(fb, fw, fh, cx_pos, prompt_y, CHAR_W // 2, CHAR_H, self.cursor_color)
|
||||
|
||||
def handle_event(self, evt: window.InputEvent*) -> int:
|
||||
etype: uint32 = evt.type
|
||||
@@ -1268,9 +1278,11 @@ virtual class Text(Widget):
|
||||
self.focused = 1
|
||||
tk: Tk* = self.tk
|
||||
tk.focused_widget = Widget*(self)
|
||||
prompt_y: int = self.y + self.h - CHAR_H - 4
|
||||
if ey >= prompt_y and ey < prompt_y + CHAR_H:
|
||||
rel_x: int = ex - self.x - 4 - 2 * CHAR_W
|
||||
prompt_row: int = self.line_count - self.scroll_offset
|
||||
prompt_y: int = self.y + prompt_row * CHAR_H
|
||||
if prompt_row >= 0 and prompt_row < self._visible_lines() and ey >= prompt_y and ey < prompt_y + CHAR_H:
|
||||
plen: int = int(string.strlen(&self.prompt[0]))
|
||||
rel_x: int = ex - self.x - 4 - plen * CHAR_W
|
||||
if rel_x < 0: rel_x = 0
|
||||
new_pos: int = (rel_x + CHAR_W / 2) / CHAR_W
|
||||
if new_pos > self.input_len: new_pos = self.input_len
|
||||
@@ -1368,7 +1380,7 @@ virtual class Text(Widget):
|
||||
return 1
|
||||
elif ch == window.VK_NEXT:
|
||||
vis2: int = self._visible_lines()
|
||||
max_off: int = self.line_count - vis2
|
||||
max_off: int = self.line_count + 1 - vis2
|
||||
if max_off < 0: max_off = 0
|
||||
if self.scroll_offset < max_off:
|
||||
self.scroll_offset += vis2
|
||||
@@ -1389,14 +1401,15 @@ virtual class Text(Widget):
|
||||
return 0
|
||||
|
||||
def _exec_input(self):
|
||||
self.insert("> ")
|
||||
self.insert(&self.prompt[0])
|
||||
plen: int = int(string.strlen(&self.prompt[0]))
|
||||
if self.input_len > 0:
|
||||
last: int = self.line_count - 1
|
||||
j: int = 0
|
||||
while j < self.input_len and j < 77:
|
||||
self.lines[last][2 + j] = self.input_buf[j]
|
||||
while j < self.input_len and plen + j < 79:
|
||||
self.lines[last][plen + j] = self.input_buf[j]
|
||||
j += 1
|
||||
self.lines[last][2 + j] = 0
|
||||
self.lines[last][plen + j] = 0
|
||||
if self.history_count < 16:
|
||||
for k in range(80):
|
||||
self.history[self.history_count][k] = self.input_buf[k]
|
||||
@@ -1487,8 +1500,9 @@ pop r12""",
|
||||
self.lines[idx][j] = 0
|
||||
self.line_count += 1
|
||||
vis: int = self._visible_lines()
|
||||
if self.line_count > vis:
|
||||
self.scroll_offset = self.line_count - vis
|
||||
total: int = self.line_count + 1 # 输出行 + 输入行
|
||||
if total > vis:
|
||||
self.scroll_offset = total - vis
|
||||
else:
|
||||
self.scroll_offset = 0
|
||||
self._update_scrollbar()
|
||||
@@ -1501,6 +1515,15 @@ pop r12""",
|
||||
def get(self) -> const str:
|
||||
return &self.input_buf[0]
|
||||
|
||||
def set_prompt(self, text: const str):
|
||||
i: int = 0
|
||||
while i < 79:
|
||||
ch: char = *(text + i)
|
||||
self.prompt[i] = ch
|
||||
if ch == 0: break
|
||||
i += 1
|
||||
self.prompt[i] = 0
|
||||
|
||||
def config(self, text_color: uint32 = None, bg_color: uint32 = None, prompt_color: uint32 = None, cursor_color: uint32 = None, border_color: uint32 = None):
|
||||
if text_color is not None:
|
||||
self.text_color = text_color
|
||||
@@ -1514,23 +1537,24 @@ pop r12""",
|
||||
self.border_color = border_color
|
||||
|
||||
def _visible_lines(self) -> int:
|
||||
return (self.h - CHAR_H - 4) / CHAR_H
|
||||
return (self.h - 2) / CHAR_H
|
||||
|
||||
def _update_scrollbar(self):
|
||||
vis: int = self._visible_lines()
|
||||
if self.line_count <= vis:
|
||||
total: int = self.line_count + 1 # 输出行 + 输入行
|
||||
if total <= vis:
|
||||
self.scroll_offset = 0
|
||||
else:
|
||||
max_off: int = self.line_count - vis
|
||||
max_off: int = total - vis
|
||||
if self.scroll_offset > max_off:
|
||||
self.scroll_offset = max_off
|
||||
if self.yscrollbar is not None:
|
||||
sb: Scrollbar* = Scrollbar*(self.yscrollbar)
|
||||
if self.line_count <= vis:
|
||||
if total <= vis:
|
||||
sb.set(0, 1000)
|
||||
else:
|
||||
first: int = self.scroll_offset * 1000 / self.line_count
|
||||
last: int = (self.scroll_offset + vis) * 1000 / self.line_count
|
||||
first: int = self.scroll_offset * 1000 / total
|
||||
last: int = (self.scroll_offset + vis) * 1000 / total
|
||||
sb.set(first, last)
|
||||
|
||||
# ---- Tk:主窗口类 = Wm(窗口管理器协议,无字段)+ Misc(公共状态/方法)----
|
||||
|
||||
Reference in new issue
Block a user