Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ jobs:
draft: false
prerelease: false

# macOS signing (optional): GitHub repo secrets
# MACOS_CERTIFICATE_BASE64 — base64 of exported .p12 ("Developer ID Application" + private key)
# MACOS_CERTIFICATE_PASSWORD — password for that .p12
# APPLE_CODESIGN_IDENTITY — full name from Keychain, e.g. "Developer ID Application: Your Name (TEAMID)"
# Notarization (optional, recommended for Gatekeeper):
# APPLE_API_KEY_ID, APPLE_API_ISSUER_ID — from App Store Connect → Users and Access → Integrations → Keys
# APPLE_API_KEY_BASE64 — base64 of the downloaded AuthKey_XXXXXX.p8 file
build-macos:
runs-on: macos-latest
continue-on-error: false
Expand Down Expand Up @@ -139,17 +146,12 @@ jobs:
echo "Found app at: $APP_PATH"

- name: Deploy Qt dependencies and create DMG
env:
APPLE_CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }}
run: |
APP_PATH="${{ steps.find_app.outputs.app_path }}"
# Deploy Qt dependencies into the app bundle
macdeployqt "$APP_PATH" -always-overwrite -verbose=2
# Create DMG
macdeployqt "$APP_PATH" -dmg -always-overwrite
DMG_PATH="${APP_PATH%.app}.dmg"
echo "DMG created at: $DMG_PATH"
ls -lh "$(dirname $DMG_PATH)"

# Verify Qt libraries are bundled
if [ -d "$APP_PATH/Contents/Frameworks" ]; then
echo "Qt frameworks bundled:"
ls -lh "$APP_PATH/Contents/Frameworks" | head -10
Expand Down
85 changes: 70 additions & 15 deletions typistprison/popups/prisonerdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,9 @@ class PrisonerDialog : public QDialog {
}
)");
} else {
QString numericText = lineEdit->text().remove(QRegularExpression("[^0-9]"));
if (numericText.isEmpty()) {
actionButton->setEnabled(false);
} else {
// When the goal is explicitly set to +∞, treat it as a valid
// word goal and allow launching, even though there are no digits.
if (lineEdit->text() == "+∞") {
actionButton->setEnabled(true);
actionButton->setIcons(QIcon(":/icons/right_arrow.png"), QIcon(":/icons/right_arrow_hover.png"));
actionButton->setSilentBehavior(R"(
Expand All @@ -288,6 +287,32 @@ class PrisonerDialog : public QDialog {
padding: 4px 8px;
}
)");
} else {
QString numericText = lineEdit->text().remove(QRegularExpression("[^0-9]"));
if (numericText.isEmpty()) {
actionButton->setEnabled(false);
} else {
actionButton->setEnabled(true);
actionButton->setIcons(QIcon(":/icons/right_arrow.png"), QIcon(":/icons/right_arrow_hover.png"));
actionButton->setSilentBehavior(R"(
QPushButton {
background-color: transparent;
border: 1px solid #5A5A5A;
color: #BDBDBD;
border-radius: 4px;
padding: 4px 8px;
}
)");
actionButton->setHoverBehavior(R"(
QPushButton {
background-color: transparent;
border: 1px solid #999999;
color: #DEDEDE;
border-radius: 4px;
padding: 4px 8px;
}
)");
}
}
}
if (ratio == 1.0) {
Expand Down Expand Up @@ -373,9 +398,15 @@ class PrisonerDialog : public QDialog {
actionButton->setLayoutDirection(Qt::RightToLeft);

connect(actionButton, &QPushButton::clicked, this, [=]() {
int wordGoal;
// Get word goal from lineEdit
QString numericText = lineEdit->text().remove(QRegularExpression("[^0-9]"));
int wordGoal = numericText.toInt();
if (lineEdit->text() == "+∞") {
wordGoal = -1;
} else {
QString numericText = lineEdit->text().remove(QRegularExpression("[^0-9]"));
wordGoal = numericText.toInt();
}


// Get time limit from slider
double ratio = slider->value() / 100.0;
Expand Down Expand Up @@ -659,21 +690,45 @@ private slots:
int totalWidth = sizes[0] + sizes[1]; // Total width of splitter

double spliterRatio = static_cast<double>(sizes[0] - minLeftWidth) / (totalWidth - minLeftWidth);
int numericValue = 0;
if (spliterRatio == 1.0) {
// Infinite word goal: show "+∞" and keep the Launch button enabled.
lineEdit->setText("+∞");
slider->setValue(100); // Set slider to maximum
slider->setEnabled(false); // Disable slider interaction

actionButton->setEnabled(true);
actionButton->setIcons(QIcon(":/icons/right_arrow.png"), QIcon(":/icons/right_arrow_hover.png"));
actionButton->setSilentBehavior(R"(
QPushButton {
background-color: transparent;
border: 1px solid #5A5A5A;
color: #BDBDBD;
border-radius: 4px;
padding: 4px 8px;
}
)");
actionButton->setHoverBehavior(R"(
QPushButton {
background-color: transparent;
border: 1px solid #999999;
color: #DEDEDE;
border-radius: 4px;
padding: 4px 8px;
}
)");
return;
} else {
double sliderRatio = static_cast<double>(sizes[0]) / totalWidth;
slider->setValue(sliderRatio * 100); // Set slider value
slider->setEnabled(true); // Enable slider interaction

// Reverse map to get the numeric value from width
numericValue = reverseMapFromRange(spliterRatio);

// Update the QLineEdit content
lineEdit->setText(QString::number(numericValue));
}
double sliderRatio = static_cast<double>(sizes[0]) / totalWidth;
slider->setValue(sliderRatio * 100); // Set slider value
slider->setEnabled(true); // Enable slider interaction

// Reverse map to get the numeric value from width
int numericValue = reverseMapFromRange(spliterRatio);

// Update the QLineEdit content
lineEdit->setText(QString::number(numericValue));

// Change visibility of actionbutton ⏯️
if (numericValue >= 100) {
Expand Down
8 changes: 8 additions & 0 deletions typistprison/prisonermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ void PrisonerManager::clear() {

void PrisonerManager::updateTimerProgress(qreal timerProgressLength) {
this->timerProgressLength = timerProgressLength;

// If wordGoal is -1, we are in "infinite words" mode:
// - Do not track background progress in the editor
// - Do not run the chaser / failure logic
if (wordGoal == -1) {
return;
}

greyWordCount = qRound(wordGoal * timerProgressLength) + baseWordCount;
if (isPrisoner) {
// if chaser reach the progress, fail the prisoner mode
Expand Down
22 changes: 18 additions & 4 deletions typistprison/progressborderwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,19 @@ void ProgressBorderWidget::deactivatePrisonerMode() {

void ProgressBorderWidget::startTimerProgress(int timeLimit, int wordGoal) {
timerProgress = 0.0;
targetWordCount = wordGoal;

// For infinite word goals (wordGoal == -1), we do not start the background
// timer/chaser at all. The visual chaser and timer-driven failure are disabled.
if (wordGoal == -1) {
totalTime = 0.0;
prisonerTimer->stop();
isTimerRunning = false;
return;
}

// convert minutes time to seconds
totalTime = timeLimit * 60 * 10;
targetWordCount = wordGoal;
prisonerTimer->start(100); // update interval
isTimerRunning = true;
}
Expand All @@ -91,7 +101,9 @@ void ProgressBorderWidget::updateTimerProgress() {
timerProgress = timerProgress + 1;
requestRepaint();

if (prisonerManager && totalTime > 0.0) {
// If there is no meaningful total time (including infinite-word mode where
// we disabled the chaser), skip notifying the PrisonerManager.
if (prisonerManager && totalTime > 0.0 && targetWordCount != -1) {
qreal normalizedProgress = timerProgress / totalTime;
prisonerManager->updateTimerProgress(normalizedProgress);
}
Expand Down Expand Up @@ -313,8 +325,10 @@ void ProgressBorderWidget::paintEvent(QPaintEvent *event) {

qreal perimeter = 2 * (width + height) - startDistance - endDistance + 3 * cornerArcLength;
qreal timerProgressLength;
if (totalTime == 0.0){
timerProgressLength = 0;
// If there is no timer (including infinite word goal where we disabled it),
// do not paint the chaser border at all.
if (totalTime <= 0.0 || targetWordCount == -1) {
timerProgressLength = -1;
} else {
timerProgressLength = (timerProgress / totalTime) * perimeter;
}
Expand Down
Loading