| 1121 | // ── Double-click hit test ── |
| 1122 | |
| 1123 | static bool hitTestTarget(QsciScintilla* sci, |
| 1124 | const QVector<LineMeta>& meta, |
| 1125 | const QPoint& viewportPos, |
| 1126 | int& outLine, int& outCol, EditTarget& outTarget) |
| 1127 | { |
| 1128 | long pos = sci->SendScintilla(QsciScintillaBase::SCI_POSITIONFROMPOINTCLOSE, |
| 1129 | (unsigned long)viewportPos.x(), (long)viewportPos.y()); |
| 1130 | if (pos < 0) return false; |
| 1131 | int line = (int)sci->SendScintilla(QsciScintillaBase::SCI_LINEFROMPOSITION, |
| 1132 | (unsigned long)pos); |
| 1133 | int col = (int)sci->SendScintilla(QsciScintillaBase::SCI_GETCOLUMN, |
| 1134 | (unsigned long)pos); |
| 1135 | outCol = col; |
| 1136 | if (line < 0 || line >= meta.size()) return false; |
| 1137 | |
| 1138 | QString lineText = getLineText(sci, line); |
| 1139 | int textLen = lineText.size(); |
| 1140 | |
| 1141 | const LineMeta& lm = meta[line]; |
| 1142 | |
| 1143 | if (lm.lineKind == LineKind::ArrayElementSeparator) return false; |
| 1144 | |
| 1145 | auto inSpan = [&](const ColumnSpan& s) { |
| 1146 | return s.valid && col >= s.start && col < s.end; |
| 1147 | }; |
| 1148 | |
| 1149 | // CommandRow: interactive chevron/SRC/ADDR + root class (type+name) |
| 1150 | if (lm.lineKind == LineKind::CommandRow) { |
| 1151 | ColumnSpan chevron = commandRowChevronSpan(lineText); |
| 1152 | if (inSpan(chevron)) { outTarget = EditTarget::TypeSelector; outLine = line; return true; } |
| 1153 | ColumnSpan ss = commandRowSrcSpan(lineText); |
| 1154 | if (inSpan(ss)) { outTarget = EditTarget::Source; outLine = line; return true; } |
| 1155 | ColumnSpan as = commandRowAddrSpan(lineText); |
| 1156 | if (inSpan(as)) { outTarget = EditTarget::BaseAddress; outLine = line; return true; } |
| 1157 | |
| 1158 | ColumnSpan rts = commandRowRootTypeSpan(lineText); |
| 1159 | if (inSpan(rts)) { outTarget = EditTarget::RootClassType; outLine = line; return true; } |
| 1160 | ColumnSpan rns = commandRowRootNameSpan(lineText); |
| 1161 | if (inSpan(rns)) { outTarget = EditTarget::RootClassName; outLine = line; return true; } |
| 1162 | return false; |
| 1163 | } |
| 1164 | |
| 1165 | // Use per-line effective widths from LineMeta |
| 1166 | int typeW = lm.effectiveTypeW; |
| 1167 | int nameW = lm.effectiveNameW; |
| 1168 | |
| 1169 | ColumnSpan ts = RcxEditor::typeSpan(lm, typeW); |
| 1170 | ColumnSpan ns = RcxEditor::nameSpan(lm, typeW, nameW); |
| 1171 | ColumnSpan vs = RcxEditor::valueSpan(lm, textLen, typeW, nameW); |
| 1172 | |
| 1173 | // Pointer fields/headers: check sub-spans within type column first |
| 1174 | if (lm.nodeKind == NodeKind::Pointer32 || lm.nodeKind == NodeKind::Pointer64) { |
| 1175 | ColumnSpan ptrTarget = pointerTargetSpanFor(lm, lineText); |
| 1176 | ColumnSpan ptrKind = pointerKindSpanFor(lm, lineText); |
| 1177 | if (inSpan(ptrTarget)) { outTarget = EditTarget::PointerTarget; outLine = line; return true; } |
| 1178 | if (inSpan(ptrKind)) { outTarget = EditTarget::Type; outLine = line; return true; } |
| 1179 | } |
| 1180 |
no test coverage detected