fix(vpui): Label 自然尺寸与 pack 交叉轴居中

Input Demo 的两个显示问题:

- Label 没有显式 w/h,构造后 w/h 恒为 0:pack 时按 0 尺寸计算,
  PACK_BOTTOM 的标签被贴到内容区底边之外(字模被裁),且不占纵向空间,
  后面的按钮与 Name: 标签重叠。新增 _fit_natural() 按文本最长行/行数
  计算自然尺寸,__init__ 与 config() 时更新。
- pack 在未 fill 时原先一律贴左上,导致 Submit 按钮紧贴左边缘。
  改为与 tkinter 默认 anchor=center 一致:TOP/BOTTOM 横向居中、
  LEFT/RIGHT 纵向居中,沿 pack 方向仍保留 padx/pady。
- redraw 先刷新窗口尺寸再 _do_layout(),避免 resize 后布局滞后一帧。
This commit is contained in:
TermiNexus committed 2026-10-26 23:04:25 +08:00
1 parent ad6f0f68a2
commit 338472e304
1 file changed
+40 -5
+40 -5
View File
@@ -596,8 +596,31 @@ virtual class Label(Widget):
self.text[i] = ch
i += 1
self.text[i] = 0
# Label 没有显式 w/h 参数:自然尺寸必须由文本推出。否则 pack 拿到的请求尺寸是
# 0×0 ⇒ 不占布局空间,且 PACK_BOTTOM/非 fill 时会被贴到可视区域之外(文字被裁掉)。
self._fit_natural()
_register_widget(master, Widget*(self))
# 按当前 text 计算自然尺寸:宽取最长行,高取行数(多行 Label 也成立)。
def _fit_natural(self):
line_chars: int = 0
max_chars: int = 0
lines: int = 1
i: int = 0
while i < 64:
ch: char = self.text[i]
if ch == 0: break
if ch == 10:
if line_chars > max_chars: max_chars = line_chars
line_chars = 0
lines += 1
else:
line_chars += 1
i += 1
if line_chars > max_chars: max_chars = line_chars
self.w = max_chars * CHAR_W
self.h = lines * CHAR_H
def draw(self, fb: uint32*, fw: int, fh: int):
_draw_text(fb, fw, fh, self.x, self.y, &self.text[0], self.color)
@@ -613,6 +636,7 @@ virtual class Label(Widget):
if ch == 0: break
i += 1
self.text[i] = 0
self._fit_natural()
if color is not None:
self.color = color
@@ -1649,7 +1673,6 @@ virtual class Tk(Wm, Misc):
# 窗口槽已回收(用户点了关闭):立即返回,不再触碰共享 arena / flush。
if self.win_id < 0: return
if window.is_active(self.win_id) == 0: return
self._do_layout()
self.buf = window.get_winbuf(self.win_id)
sz: int = window.get_win_size(self.win_id)
if sz != 0:
@@ -1657,6 +1680,8 @@ virtual class Tk(Wm, Misc):
self.bh = sz & 0xFFFF
if not self.buf: return
if self.bw <= 0 or self.bh <= 0: return
# 先刷新尺寸再布局:窗口 resize 后若沿用上一帧的 bw/bh,布局会滞后一帧。
self._do_layout()
# 【诊断·临时】每个进程第一次真正走到 redraw 时打一行:证明"app 侧开始绘制了",
# 并给出它拿到的缓冲地址/尺寸(与 explorer 侧 shm0/flush 对照)。
# `_first` 只在本帧为 1:下面的 st1..st4 分段打点也只打这一次,用于定位
@@ -1742,13 +1767,17 @@ virtual class Tk(Wm, Misc):
py: int = _g_wl_pady[i]
ww: int = w.w
wh: int = w.h
# 交叉轴上未 fill 时按 Tk 默认 anchor=center 居中;沿 pack 方向仍贴腔体边缘 +
# padding(与 tkinter 一致)。此前一律贴左上,导致不 fill 的控件全部挤在左边缘。
if side == PACK_TOP:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
w.x = cav_x + px
else:
w.x = cav_x + px + (cav_w - 2 * px - ww) / 2
if expand:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
w.x = cav_x + px
w.y = cav_y + py
w.w = ww
w.h = wh
@@ -1757,10 +1786,12 @@ virtual class Tk(Wm, Misc):
elif side == PACK_BOTTOM:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
w.x = cav_x + px
else:
w.x = cav_x + px + (cav_w - 2 * px - ww) / 2
if expand:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
w.x = cav_x + px
w.y = cav_y + cav_h - wh - py
w.w = ww
w.h = wh
@@ -1768,11 +1799,13 @@ virtual class Tk(Wm, Misc):
elif side == PACK_LEFT:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
w.y = cav_y + py
else:
w.y = cav_y + py + (cav_h - 2 * py - wh) / 2
if expand:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
w.x = cav_x + px
w.y = cav_y + py
w.w = ww
w.h = wh
cav_x += ww + 2 * px
@@ -1780,11 +1813,13 @@ virtual class Tk(Wm, Misc):
elif side == PACK_RIGHT:
if fill == FILL_Y or fill == FILL_BOTH:
wh = cav_h - 2 * py
w.y = cav_y + py
else:
w.y = cav_y + py + (cav_h - 2 * py - wh) / 2
if expand:
if fill == FILL_X or fill == FILL_BOTH:
ww = cav_w - 2 * px
w.x = cav_x + cav_w - ww - px
w.y = cav_y + py
w.w = ww
w.h = wh
cav_w -= ww + 2 * px