Skip to content

Commit 7da9c0a

Browse files
committed
click-drag in the line number area now drag-selects whole lines
1 parent 431c957 commit 7da9c0a

3 files changed

Lines changed: 160 additions & 24 deletions

File tree

QtSLiM/QtSLiMScriptTextEdit.cpp

Lines changed: 149 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ void QtSLiMTextEdit::selfInit(void)
101101
connect(this, &QPlainTextEdit::selectionChanged, this, &QtSLiMTextEdit::updateStatusFieldFromSelection);
102102
connect(this, &QPlainTextEdit::cursorPositionChanged, this, &QtSLiMTextEdit::updateStatusFieldFromSelection);
103103

104+
// BCH 28 August 2025: get rid of the annoying insertion point remnant shown briefly when the selection changes
105+
// this is a dangerous change since it could, if there is a bug somewhere, make the insertion point disappear!
106+
connect(this, &QPlainTextEdit::selectionChanged, this, [this]() { if (textCursor().hasSelection()) setCursorWidth(0); else setCursorWidth(1); });
107+
connect(this, &QPlainTextEdit::cursorPositionChanged, this, [this]() { if (textCursor().hasSelection()) setCursorWidth(0); else setCursorWidth(1); });
108+
104109
// Wire up to change the font when the display font pref changes
105110
QtSLiMPreferencesNotifier &prefsNotifier = QtSLiMPreferencesNotifier::instance();
106111

@@ -2525,7 +2530,9 @@ class LineNumberArea : public QWidget
25252530
protected:
25262531
virtual bool event(QEvent *p_event) override;
25272532
virtual void paintEvent(QPaintEvent *p_paintEvent) override { codeEditor->lineNumberAreaPaintEvent(p_paintEvent); }
2528-
virtual void mousePressEvent(QMouseEvent *p_mouseEvent) override { codeEditor->lineNumberAreaMouseEvent(p_mouseEvent); }
2533+
virtual void mousePressEvent(QMouseEvent *p_mouseEvent) override { codeEditor->lineNumberAreaMousePressEvent(p_mouseEvent); }
2534+
virtual void mouseMoveEvent(QMouseEvent *p_mouseEvent) override { codeEditor->lineNumberAreaMouseMoveEvent(p_mouseEvent); }
2535+
virtual void mouseReleaseEvent(QMouseEvent *p_mouseEvent) override { codeEditor->lineNumberAreaMouseReleaseEvent(p_mouseEvent); }
25292536
virtual void contextMenuEvent(QContextMenuEvent *p_event) override { codeEditor->lineNumberAreaContextMenuEvent(p_event); }
25302537
virtual void wheelEvent(QWheelEvent *p_wheelEvent) override { codeEditor->lineNumberAreaWheelEvent(p_wheelEvent); }
25312538

@@ -2583,8 +2590,10 @@ void QtSLiMScriptTextEdit::initializeLineNumbers(void)
25832590
connect(this->document(), SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
25842591
connect(this->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(updateLineNumberArea(int)));
25852592
connect(this, SIGNAL(textChanged()), this, SLOT(updateLineNumberArea()));
2593+
connect(this, SIGNAL(selectionChanged()), this, SLOT(updateLineNumberArea()));
25862594
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(updateLineNumberArea()));
25872595

2596+
connect(this, &QtSLiMScriptTextEdit::selectionChanged, this, &QtSLiMScriptTextEdit::highlightCurrentLine);
25882597
connect(this, &QtSLiMScriptTextEdit::cursorPositionChanged, this, &QtSLiMScriptTextEdit::highlightCurrentLine);
25892598
connect(qtSLiMAppDelegate, &QtSLiMAppDelegate::applicationPaletteChanged, this, &QtSLiMScriptTextEdit::highlightCurrentLine);
25902599

@@ -2938,6 +2947,43 @@ void QtSLiMScriptTextEdit::toggleDebuggingForLine(int lineNumber)
29382947
updateDebugPoints();
29392948
}
29402949

2950+
void QtSLiMScriptTextEdit::trackLineSelection(int lineNumber, int anchorLineNumber)
2951+
{
2952+
QTextDocument *doc = document();
2953+
QTextBlock block = doc->findBlockByNumber(lineNumber);
2954+
QTextCursor block_tc(block);
2955+
QTextBlock anchor_block = doc->findBlockByNumber(anchorLineNumber);
2956+
QTextCursor anchor_tc(anchor_block);
2957+
2958+
block_tc.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor, 1);
2959+
block_tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 1);
2960+
2961+
int block_start = block_tc.selectionStart();
2962+
int block_end = block_tc.selectionEnd();
2963+
2964+
anchor_tc.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor, 1);
2965+
anchor_tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 1);
2966+
2967+
int anchor_start = anchor_tc.selectionStart();
2968+
int anchor_end = anchor_tc.selectionEnd();
2969+
2970+
QTextCursor selection_tc(anchor_tc);
2971+
2972+
if (anchor_start <= block_start)
2973+
{
2974+
// the anchor is above the final line, so select from the anchor start to the final end
2975+
selection_tc.setPosition(block_end, QTextCursor::KeepAnchor);
2976+
}
2977+
else
2978+
{
2979+
// the anchor is below the final line, so select from the anchor end to the final start
2980+
selection_tc.setPosition(anchor_end, QTextCursor::MoveAnchor);
2981+
selection_tc.setPosition(block_start, QTextCursor::KeepAnchor);
2982+
}
2983+
2984+
setTextCursor(selection_tc);
2985+
}
2986+
29412987
void QtSLiMScriptTextEdit::updateDebugPoints(void)
29422988
{
29432989
// prevent re-entrancy
@@ -3066,7 +3112,22 @@ void QtSLiMScriptTextEdit::highlightCurrentLine()
30663112
selection.format.setBackground(inDarkMode ? lineHighlightColor_DARK : lineHighlightColor);
30673113
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
30683114
selection.cursor = textCursor();
3069-
selection.cursor.clearSelection();
3115+
3116+
if (selection.cursor.hasSelection())
3117+
{
3118+
// with a selection, we want to try to highlight the appropriate range
3119+
// if the selection ends at a newline, we don't want to highlight the
3120+
// next line; want want to highlight the lines that contain visible text
3121+
int start = selection.cursor.selectionStart();
3122+
3123+
selection.cursor.setPosition(start, QTextCursor::MoveAnchor);
3124+
3125+
// still have a movement bug when the selection changes, the highlight doesn't update correctly in some cases
3126+
// check when/how the message is sent
3127+
}
3128+
3129+
qDebug() << "highlightCurrentLine() with position" << selection.cursor.selectionStart();
3130+
30703131
extra_selections.append(selection);
30713132
}
30723133

@@ -3272,29 +3333,12 @@ void QtSLiMScriptTextEdit::lineNumberAreaPaintEvent(QPaintEvent *p_paintEvent)
32723333
painter.restore();
32733334
}
32743335

3275-
void QtSLiMScriptTextEdit::lineNumberAreaMouseEvent(QMouseEvent *p_mouseEvent)
3336+
int QtSLiMScriptTextEdit::_blockNumberForLocalY(QPointF localPos)
32763337
{
3277-
if (lineNumberAreaBugWidth == 0)
3278-
return;
3279-
3280-
// For some reason, Qt calls mousePressEvent() first for control-clicks and right-clicks,
3281-
// and *then* calls contextMenuEvent(), so we need to detect the context menu situation
3282-
// and return without doing anything. Note that Qt::RightButton is set for control-clicks!
3283-
if (p_mouseEvent->button() == Qt::RightButton)
3284-
return;
3285-
3286-
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
3287-
QPointF localPos = p_mouseEvent->localPos();
3288-
#else
3289-
QPointF localPos = p_mouseEvent->position();
3290-
#endif
32913338
qreal localY = localPos.y();
32923339

32933340
//qDebug() << "localY ==" << localY;
32943341

3295-
if ((localPos.x() < 0) || (localPos.x() >= lineNumberAreaBugWidth + 2)) // +2 for a little slop
3296-
return;
3297-
32983342
// Find the position of the click in the document. We loop through the blocks manually.
32993343
QTextBlock block = firstVisibleBlock();
33003344
int blockNumber = block.blockNumber();
@@ -3307,12 +3351,11 @@ void QtSLiMScriptTextEdit::lineNumberAreaMouseEvent(QMouseEvent *p_mouseEvent)
33073351
{
33083352
if ((localY >= top) && (localY <= bottom))
33093353
{
3310-
toggleDebuggingForLine(blockNumber);
3311-
break;
3354+
return blockNumber;
33123355
}
33133356
else if (localY < top)
33143357
{
3315-
break;
3358+
return -1;
33163359
}
33173360
}
33183361

@@ -3321,6 +3364,89 @@ void QtSLiMScriptTextEdit::lineNumberAreaMouseEvent(QMouseEvent *p_mouseEvent)
33213364
bottom = top + qRound(blockBoundingRect(block).height());
33223365
++blockNumber;
33233366
}
3367+
3368+
return -1;
3369+
}
3370+
3371+
void QtSLiMScriptTextEdit::lineNumberAreaMousePressEvent(QMouseEvent *p_mouseEvent)
3372+
{
3373+
if (lineNumberAreaBugWidth == 0)
3374+
return;
3375+
3376+
// For some reason, Qt calls mousePressEvent() first for control-clicks and right-clicks,
3377+
// and *then* calls contextMenuEvent(), so we need to detect the context menu situation
3378+
// and return without doing anything. Note that Qt::RightButton is set for control-clicks!
3379+
if (p_mouseEvent->button() == Qt::RightButton)
3380+
return;
3381+
3382+
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
3383+
QPointF localPos = p_mouseEvent->localPos();
3384+
#else
3385+
QPointF localPos = p_mouseEvent->position();
3386+
#endif
3387+
3388+
if (localPos.x() < 0)
3389+
return;
3390+
3391+
int blockNumber = _blockNumberForLocalY(localPos);
3392+
3393+
if (blockNumber != -1)
3394+
{
3395+
if (localPos.x() >= lineNumberAreaBugWidth)
3396+
{
3397+
// This is a click in the line number area, so select the line
3398+
trackingLineSelection = true;
3399+
trackingLineAnchor = blockNumber;
3400+
3401+
trackLineSelection(blockNumber, trackingLineAnchor);
3402+
}
3403+
else
3404+
{
3405+
// This is a click in the debug point column, so toggle debugging
3406+
toggleDebuggingForLine(blockNumber);
3407+
}
3408+
}
3409+
}
3410+
3411+
void QtSLiMScriptTextEdit::lineNumberAreaMouseMoveEvent(QMouseEvent *p_mouseEvent)
3412+
{
3413+
if (!trackingLineSelection)
3414+
return;
3415+
3416+
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
3417+
QPointF localPos = p_mouseEvent->localPos();
3418+
#else
3419+
QPointF localPos = p_mouseEvent->position();
3420+
#endif
3421+
3422+
int blockNumber = _blockNumberForLocalY(localPos);
3423+
3424+
if (blockNumber != -1)
3425+
{
3426+
trackLineSelection(blockNumber, trackingLineAnchor);
3427+
}
3428+
}
3429+
3430+
void QtSLiMScriptTextEdit::lineNumberAreaMouseReleaseEvent(QMouseEvent *p_mouseEvent)
3431+
{
3432+
if (!trackingLineSelection)
3433+
return;
3434+
3435+
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
3436+
QPointF localPos = p_mouseEvent->localPos();
3437+
#else
3438+
QPointF localPos = p_mouseEvent->position();
3439+
#endif
3440+
3441+
int blockNumber = _blockNumberForLocalY(localPos);
3442+
3443+
if (blockNumber != -1)
3444+
{
3445+
trackLineSelection(blockNumber, trackingLineAnchor);
3446+
}
3447+
3448+
trackingLineSelection = false;
3449+
trackingLineAnchor = -1;
33243450
}
33253451

33263452
void QtSLiMScriptTextEdit::lineNumberAreaContextMenuEvent(QContextMenuEvent *p_event)

QtSLiM/QtSLiMScriptTextEdit.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,16 @@ public slots:
208208

209209
protected:
210210
QStringList linesForRoundedSelection(QTextCursor &cursor, bool &movedBack);
211+
int _blockNumberForLocalY(QPointF localPos);
211212

212213
// From here down is the machinery for providing line numbers
213214
// This code is adapted from https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html
214215
public:
215216
void lineNumberAreaToolTipEvent(QHelpEvent *p_helpEvent);
216217
void lineNumberAreaPaintEvent(QPaintEvent *p_paintEvent);
217-
void lineNumberAreaMouseEvent(QMouseEvent *p_mouseEvent);
218+
void lineNumberAreaMousePressEvent(QMouseEvent *p_mouseEvent);
219+
void lineNumberAreaMouseMoveEvent(QMouseEvent *p_mouseEvent);
220+
void lineNumberAreaMouseReleaseEvent(QMouseEvent *p_mouseEvent);
218221
void lineNumberAreaContextMenuEvent(QContextMenuEvent *p_event);
219222
void lineNumberAreaWheelEvent(QWheelEvent *p_wheelEvent);
220223
int lineNumberAreaWidth(void);
@@ -251,6 +254,12 @@ private slots:
251254

252255
void toggleDebuggingForLine(int lineNumber);
253256

257+
// Line selection by clicking in the line area
258+
void trackLineSelection(int lineNumber, int anchorLineNumber);
259+
260+
bool trackingLineSelection = false;
261+
int trackingLineAnchor = 0;
262+
254263
// Species coloring
255264
std::vector<QTextCursor> blockCursors; // we use QTextCursor to maintain the positions of script blocks across edits
256265
std::vector<Species *> blockSpecies; // the corresponding Species object for each block

VERSIONS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ development head (in the master branch):
2525
fix #547, mutation trajectories plot is confusing in the way it starts collecting data at the time it is opened
2626
fix #534, support uniparentally-transmitted chromosomes in hermaphrodite populations
2727
add new Plot legendTitleEntry() method for making a title line in a plot legend
28+
click-drag in the SLiMgui script view's line number area now drag-selects whole lines
2829

2930

3031
version 5.0 (Eidos version 4.0):

0 commit comments

Comments
 (0)