Bläddra i källkod

上下限贴着运算符

上一版只把上下限的字号改小了,间距并没动 —— 间距来自另外三处:

  · 每个槽自带的 3px 上下内边距(上下限传 0 收掉)
  · 字体默认行高约 1.16em,37 号字上下各多出好几个像素的空白
    (大运算符字形加 height: 1.0 压掉)
  · 再往运算符方向拽 3px(Transform.translate,不改布局)

lim 的条件行同样处理。真机确认:Infinity 紧贴 Σ 上方、1 紧贴下方,
lim 的 x→0 贴住。

Co-Authored-By: Claude Code <noreply@anthropic.com>
nonvioce 1 månad sedan
förälder
incheckning
cb098d7bdd
1 ändrade filer med 26 tillägg och 9 borttagningar
  1. 26 9
      lib/expr_editor.dart

+ 26 - 9
lib/expr_editor.dart

@@ -108,12 +108,16 @@ class _SlotView extends StatelessWidget {
     required this.onTapNode,
     required this.onLongPressNode,
     required this.onTapSlot,
+    this.vpad = 3,
   });
 
   final ExprDoc doc;
   final SeqNode seq;
   final SlotPath path;
   final double size;
+
+  /// 上下内边距。上下限这种要紧贴运算符的地方传 0。
+  final double vpad;
   final void Function(SlotPath parent, int index, bool before) onTapNode;
   final void Function(SlotPath parent, int index) onLongPressNode;
   final void Function(SlotPath path, SeqNode seq) onTapSlot;
@@ -131,7 +135,7 @@ class _SlotView extends StatelessWidget {
       onTap: () => onTapSlot(path, seq),
       behavior: HitTestBehavior.opaque,
       child: Container(
-        padding: const EdgeInsets.symmetric(horizontal: 2, vertical: 3),
+        padding: EdgeInsets.symmetric(horizontal: 2, vertical: vpad),
         // 空槽只有一个光标那么宽(2.5px),手指点不到 —— 而"点光标处弹粘贴"
         // 恰恰最常在空槽里用(先按 ln 再粘参数)。给个最小的可点面积。
         constraints: focused && seq.isEmpty
@@ -414,16 +418,26 @@ class _NodeView extends StatelessWidget {
   Widget _bigOp(BuildContext context, BigOpNode b) {
     final body = _slot(context, b.body, parent.push(index, 0));
     final variable = _slot(context, b.variable, parent.push(index, 1));
-    final lower = _slot(context, b.lower, parent.push(index, 2), scale: 0.5);
-    final upper = _slot(context, b.upper, parent.push(index, 3), scale: 0.5);
+    // 上下限紧贴运算符:槽的上下内边距收掉,再往中间拽几像素
+    final lower = Transform.translate(
+      offset: const Offset(0, -3),
+      child: _slot(context, b.lower, parent.push(index, 2),
+          scale: 0.5, vpad: 0),
+    );
+    final upper = Transform.translate(
+      offset: const Offset(0, 3),
+      child: _slot(context, b.upper, parent.push(index, 3),
+          scale: 0.5, vpad: 0),
+    );
 
     if (b.kind == 'Limit') {
       // lim 下面写"x→0"
       final under = Row(
         mainAxisSize: MainAxisSize.min,
         children: [
-          _slot(context, b.variable, parent.push(index, 1), scale: 0.6),
-          Text('→', style: _mathStyle(context, 0.6)),
+          _slot(context, b.variable, parent.push(index, 1),
+              scale: 0.6, vpad: 0),
+          Text('→', style: _mathStyle(context, 0.6).copyWith(height: 1.0)),
           lower,
         ],
       );
@@ -431,8 +445,8 @@ class _NodeView extends StatelessWidget {
         Column(
           mainAxisSize: MainAxisSize.min,
           children: [
-            Text('lim', style: _mathStyle(context, 0.95)),
-            under,
+            Text('lim', style: _mathStyle(context, 0.95).copyWith(height: 1.0)),
+            Transform.translate(offset: const Offset(0, -2), child: under),
           ],
         ),
         body,
@@ -446,13 +460,15 @@ class _NodeView extends StatelessWidget {
       _ => '∑',
     };
 
+    // height: 1.0 把字体自带的行距空白压掉 —— 默认行高约 1.16em,
+    // 37 号字上下各多出好几个像素,看着就是"离运算符很远"。
     final stack = Column(
       mainAxisSize: MainAxisSize.min,
       children: [
         // 上下限**始终渲染** —— 空的时候画成小占位框。
         // 不渲染的话不定积分的两个限槽就成了够不着的死结构,永远填不进去。
         upper,
-        Text(glyph, style: _mathStyle(context, 1.7)),
+        Text(glyph, style: _mathStyle(context, 1.7).copyWith(height: 1.0)),
         lower,
       ],
     );
@@ -478,12 +494,13 @@ class _NodeView extends StatelessWidget {
   /// Transform 只改视觉、不改布局,占位仍是原尺寸 —— 结果就是"字看着小了
   /// 但离运算符老远"(用户实测反馈)。直接把 size 传下去,布局才会跟着缩。
   Widget _slot(BuildContext context, SeqNode seq, SlotPath path,
-          {double scale = 1}) =>
+          {double scale = 1, double vpad = 3}) =>
       _SlotView(
         doc: doc,
         seq: seq,
         path: path,
         size: size * scale,
+        vpad: vpad,
         onTapNode: onTapNode,
         onLongPressNode: onLongPressNode,
         onTapSlot: onTapSlot,