Skip to content

Commit

Permalink
add display of the selected block to the Jump button
Browse files Browse the repository at this point in the history
  • Loading branch information
bhaller committed Jul 18, 2024
1 parent 639dc29 commit 7e0e0e3
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 11 deletions.
48 changes: 48 additions & 0 deletions QtSLiM/QtSLiMExtras.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,54 @@ void QtSLiMFlashHighlightInTextEdit(QPlainTextEdit *te)
QTimer::singleShot(delayMillisec * 3, te, [te]() { te->setPalette(qApp->palette(te)); });
}

// A QLabel that shows shortened text with an ellipsis; see https://stackoverflow.com/a/73316405/2752221
QtSLiMEllipsisLabel::QtSLiMEllipsisLabel(QWidget *parent)
: QtSLiMEllipsisLabel("", parent)
{
}

QtSLiMEllipsisLabel::QtSLiMEllipsisLabel(QString text, QWidget *parent)
: QLabel(parent)
{
setText(text);
}

void QtSLiMEllipsisLabel::setText(QString text)
{
m_text = text;
updateText();
}

QSize QtSLiMEllipsisLabel::minimumSizeHint() const
{
return QSize(0, QLabel::minimumSizeHint().height());
}

void QtSLiMEllipsisLabel::resizeEvent(QResizeEvent *p_event)
{
QLabel::resizeEvent(p_event);
updateText();
}

void QtSLiMEllipsisLabel::updateText()
{
QFontMetrics metrics(font());
QString elided = metrics.elidedText(m_text, Qt::ElideRight, width());
QLabel::setText(elided);
}

void QtSLiMEllipsisLabel::mousePressEvent(QMouseEvent *p_event)
{
// check the mouse position and only take the click if it is within the displayed label's extent
QPoint curPoint = p_event->pos();
int clickX = curPoint.x();

QFontMetrics metrics(font());
int labelLength = metrics.size(0, text()).width();

if ((clickX >= 0) && (clickX <= labelLength + 1))
emit pressed(); // triggers QtSLiMWindow::jumpToPopupButtonPressed()
}



Expand Down
24 changes: 24 additions & 0 deletions QtSLiM/QtSLiMExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <QSplitterHandle>
#include <QStatusBar>
#include <QPlainTextEdit>
#include <QLabel>

#include <cmath>
#include <algorithm>
Expand Down Expand Up @@ -238,6 +239,29 @@ QPixmap QtSLiMDarkenPixmap(QPixmap p_pixmap);

void QtSLiMFlashHighlightInTextEdit(QPlainTextEdit *te);

// A QLabel subclass that shows shortened text with an ellipsis; see https://stackoverflow.com/a/73316405/2752221
class QtSLiMEllipsisLabel : public QLabel
{
Q_OBJECT

public:
explicit QtSLiMEllipsisLabel(QWidget *parent = nullptr);
explicit QtSLiMEllipsisLabel(QString text, QWidget *parent = nullptr);
void setText(QString);
virtual QSize minimumSizeHint() const;

signals:
void pressed(void);

protected:
void resizeEvent(QResizeEvent *p_event);
void mousePressEvent(QMouseEvent *p_event);

private:
void updateText();
QString m_text;
};


// Incremental sorting
//
Expand Down
6 changes: 6 additions & 0 deletions QtSLiM/QtSLiMScriptTextEdit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,12 @@ void QtSLiMTextEdit::updateStatusFieldFromSelection(void)
{
statusBar->clearMessage();
}

// show the script block's declaration to the right of the Jump button
QtSLiMWindow *windowSLiMController = dynamic_cast<QtSLiMWindow *>(window());

if (windowSLiMController)
windowSLiMController->setScriptBlockLabelTextFromSelection();
}
}

Expand Down
155 changes: 155 additions & 0 deletions QtSLiM/QtSLiMWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,12 @@ void QtSLiMWindow::initializeUI(void)
ui->outputTextEdit->setScriptType(QtSLiMTextEdit::NoScriptType);
ui->outputTextEdit->setSyntaxHighlightType(QtSLiMTextEdit::OutputHighlighting);

// set up the script block label, to the right of the Jump menu
QtSLiMPreferencesNotifier &prefsNotifier = QtSLiMPreferencesNotifier::instance();

connect(&prefsNotifier, &QtSLiMPreferencesNotifier::displayFontPrefChanged, this, &QtSLiMWindow::displayFontPrefChanged);
displayFontPrefChanged();

// set button states
ui->toggleDrawerButton->setChecked(false);

Expand Down Expand Up @@ -737,6 +743,17 @@ void QtSLiMWindow::initializeUI(void)
connect(ui->menuWindow, &QMenu::aboutToShow, this, &QtSLiMWindow::updateWindowMenu);
}

void QtSLiMWindow::displayFontPrefChanged(void)
{
// Xcode doesn't use its monospace for this, and it does look a bit out of place in the UI
// So let's try it allowing the font to remain the default system font...?
// QtSLiMPreferencesNotifier &prefs = QtSLiMPreferencesNotifier::instance();
// QFont displayFont = prefs.displayFontPref(nullptr);

// displayFont.setPointSize(13);
// ui->scriptBlockLabel->setFont(displayFont);
}

void QtSLiMWindow::applicationPaletteChanged(void)
{
bool inDarkMode = QtSLiMInDarkMode();
Expand Down Expand Up @@ -5555,6 +5572,144 @@ void QtSLiMWindow::jumpToPopupButtonRunMenu(void)
jumpToPopupButtonReleased();
}

void QtSLiMWindow::setScriptBlockLabelTextFromSelection(void)
{
// this does a subset of the parsing logic of QtSLiMWindow::jumpToPopupButtonRunMenu()
// it is used to get the label text for the script block label, to the right of the Jump button
QPlainTextEdit *scriptTE = ui->scriptTextEdit;
QString currentScriptString = scriptTE->toPlainText();
QByteArray utf8bytes = currentScriptString.toUtf8();
const char *cstr = utf8bytes.constData();

QTextCursor selection_cursor(scriptTE->textCursor());
int selStart = selection_cursor.selectionStart();
int selEnd = selection_cursor.selectionEnd();

if (cstr)
{
// Figure out whether we have multispecies avatars, and thus want to use the "low brightness symbol" emoji for "ticks all" blocks.
// This emoji provides nicely lined up spacing in the menu, and indicates "ticks all" clearly; seems better than nothing. It would
// be even better, perhaps, to have a spacer of emoji width, to make things line up without having a symbol displayed; unfortunately
// such a spacer does not seem to exist. https://stackoverflow.com/questions/66496671/is-there-a-blank-unicode-character-matching-emoji-width
QString ticksAllAvatar;

if (community && community->is_explicit_species_ && (community->all_species_.size() > 0))
{
bool hasAvatars = false;

for (Species *species : community->all_species_)
if (species->avatar_.length() > 0)
hasAvatars = true;

if (hasAvatars)
ticksAllAvatar = QString::fromUtf8("\xF0\x9F\x94\x85"); // "low brightness symbol", https://www.compart.com/en/unicode/U+1F505
}

SLiMEidosScript script(cstr);

try {
script.Tokenize(true, false); // make bad tokens as needed, do not keep nonsignificant tokens
script.ParseSLiMFileToAST(true); // make bad nodes as needed (i.e. never raise, and produce a correct tree)

// Extract SLiMEidosBlocks from the parse tree
const EidosASTNode *root_node = script.AST();
QString specifierAvatar;

for (EidosASTNode *script_block_node : root_node->children_)
{
// handle species/ticks specifiers, which are identifier token nodes at the top level of the AST with one child
if ((script_block_node->token_->token_type_ == EidosTokenType::kTokenIdentifier) && (script_block_node->children_.size() == 1))
{
EidosASTNode *specifierChild = script_block_node->children_[0];
std::string specifierSpeciesName = specifierChild->token_->token_string_;
Species *specifierSpecies = (community ? community->SpeciesWithName(specifierSpeciesName) : nullptr);

if (specifierSpecies && specifierSpecies->avatar_.length())
specifierAvatar = QString::fromStdString(specifierSpecies->avatar_);
else if (!specifierSpecies && (specifierSpeciesName == "all"))
specifierAvatar = ticksAllAvatar;

continue;
}

// Create the block and use it to find the string from the start of its declaration to the start of its code
SLiMEidosBlock *new_script_block = new SLiMEidosBlock(script_block_node);
int32_t decl_start = new_script_block->root_node_->token_->token_UTF16_start_;
int32_t code_end = new_script_block->compound_statement_node_->token_->token_UTF16_end_;

if ((selStart >= decl_start) && (selStart <= code_end) && (selEnd <= code_end + 2)) // +2 allows a selection through the end brace and one more character (typically a newline)
{
int32_t code_start = new_script_block->compound_statement_node_->token_->token_UTF16_start_;
QString decl = currentScriptString.mid(decl_start, code_start - decl_start);

// Remove everything including and after the first newline
if (decl.indexOf(QChar::LineFeed) != -1)
decl.truncate(decl.indexOf(QChar::LineFeed));
if (decl.indexOf(0x0C) != -1) // form feed; apparently QChar::FormFeed did not exist in older Qt versions
decl.truncate(decl.indexOf(0x0C));
if (decl.indexOf(QChar::CarriageReturn) != -1)
decl.truncate(decl.indexOf(QChar::CarriageReturn));
if (decl.indexOf(QChar::ParagraphSeparator) != -1)
decl.truncate(decl.indexOf(QChar::ParagraphSeparator));
if (decl.indexOf(QChar::LineSeparator) != -1)
decl.truncate(decl.indexOf(QChar::LineSeparator));

// Extract a comment at the end and put it after a em-dash in the string
int simpleCommentStart = decl.indexOf("//");
int blockCommentStart = decl.indexOf("/*");
QString comment;

if ((simpleCommentStart != -1) && ((blockCommentStart == -1) || (simpleCommentStart < blockCommentStart)))
{
// extract a simple comment
comment = decl.right(decl.length() - simpleCommentStart - 2);
decl.truncate(simpleCommentStart);
}
else if ((blockCommentStart != -1) && ((simpleCommentStart == -1) || (blockCommentStart < simpleCommentStart)))
{
// extract a block comment
comment = decl.right(decl.length() - blockCommentStart - 2);
decl.truncate(blockCommentStart);

int blockCommentEnd = comment.indexOf("*/");

if (blockCommentEnd != -1)
comment.truncate(blockCommentEnd);
}

// Calculate the end of the declaration string; trim off whitespace at the end
decl = decl.trimmed();

// Remove trailing whitespace, replace tabs with spaces, etc.
decl = decl.simplified();
comment = comment.trimmed();

if (comment.length() > 0)
decl = decl + "" + comment;

// If a species/ticks specifier was previously seen that provides us with an avatar, prepend that
if (specifierAvatar.length())
{
decl = specifierAvatar + " " + decl;
specifierAvatar.clear();
}

delete new_script_block;

ui->scriptBlockLabel->setText(decl);
}

delete new_script_block;
}
}
catch (...)
{
}
}

ui->scriptBlockLabel->setText(QString(""));
}

void QtSLiMWindow::clearOutputClicked(void)
{
isTransient = false; // Since the user has taken an interest in the window, clear the document's transient status
Expand Down
2 changes: 2 additions & 0 deletions QtSLiM/QtSLiMWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ class QtSLiMWindow : public QMainWindow
bool changedSinceRecycle(void);
void resetSLiMChangeCount(void);
void scriptTexteditChanged(void);
void setScriptBlockLabelTextFromSelection(void);

bool checkScriptSuppressSuccessResponse(bool suppressSuccessResponse);

Expand Down Expand Up @@ -284,6 +285,7 @@ public slots:
//

private slots:
void displayFontPrefChanged(void);
void applicationPaletteChanged(void);

bool save(void);
Expand Down
27 changes: 16 additions & 11 deletions QtSLiM/QtSLiMWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ QSlider::handle:horizontal:disabled {
<item>
<widget class="QLabel" name="scriptHeaderLabel">
<property name="text">
<string>Input Commands:</string>
<string>Input Script:</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -1050,17 +1050,17 @@ QSlider::handle:horizontal:disabled {
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QtSLiMEllipsisLabel" name="scriptBlockLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>10</height>
</size>
<property name="text">
<string>1 early()</string>
</property>
</spacer>
</widget>
</item>
</layout>
</item>
Expand Down Expand Up @@ -1300,7 +1300,7 @@ QSlider::handle:horizontal:disabled {
<x>0</x>
<y>0</y>
<width>914</width>
<height>22</height>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
Expand Down Expand Up @@ -2080,6 +2080,11 @@ QSlider::handle:horizontal:disabled {
<header>QTabBar.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QtSLiMEllipsisLabel</class>
<extends>QLabel</extends>
<header>QtSLiMExtras.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="buttons.qrc"/>
Expand Down
2 changes: 2 additions & 0 deletions QtSLiM/QtSLiMWindow_glue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ void QtSLiMWindow::glueUI(void)
connect(ui->browserButton, &QPushButton::released, this, &QtSLiMWindow::showBrowserReleased);
connect(ui->jumpToPopupButton, &QPushButton::pressed, this, &QtSLiMWindow::jumpToPopupButtonPressed);
connect(ui->jumpToPopupButton, &QPushButton::released, this, &QtSLiMWindow::jumpToPopupButtonReleased);
connect(ui->scriptBlockLabel, &QtSLiMEllipsisLabel::pressed, this, &QtSLiMWindow::jumpToPopupButtonPressed);
//connect(ui->scriptBlockLabel, &QtSLiMEllipsisLabel::released, this, &QtSLiMWindow::jumpToPopupButtonReleased); // seems to be unnecessary
connect(ui->clearOutputButton, &QPushButton::pressed, this, &QtSLiMWindow::clearOutputPressed);
connect(ui->clearOutputButton, &QPushButton::released, this, &QtSLiMWindow::clearOutputReleased);
connect(ui->dumpPopulationButton, &QPushButton::pressed, this, &QtSLiMWindow::dumpPopulationPressed);
Expand Down
1 change: 1 addition & 0 deletions VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ development head (in the master branch):
add subpopulationsWithNames() method to Community, to facilitate being able to use user-defined names for subpops
fix bugs in some graphs in SLiMgui that could cause incorrect display or a crash in various circumstances
send debug output to the main window as well as the debug output tab, to avoid important messages getting lost
add a label next to the Jump button that shows the declaration of the script block containing the selection; clicking it runs the Jump menu


version 4.2.2 (Eidos version 3.2.2):
Expand Down

0 comments on commit 7e0e0e3

Please sign in to comment.