Skip to content

Collapse engine patch/undo/status into one dynamic button#37

Closed
RobThePCGuy wants to merge 1 commit into
feat/sideload-modulefrom
feat/engine-single-button
Closed

Collapse engine patch/undo/status into one dynamic button#37
RobThePCGuy wants to merge 1 commit into
feat/sideload-modulefrom
feat/engine-single-button

Conversation

@RobThePCGuy

Copy link
Copy Markdown
Owner

Replaces the three engine widgets — Patch button, Undo button, and the status label — with a single button whose label, colour, tooltip, and action all reflect the live engine state:

State Button
Not patched "Patch BlueStacks Engine (required for root)" (red) → patches
Patched "✓ Engine patched — click to Undo (restore originals)" (green) → restores
Partially patched "⚠ Engine partially patched — click to finish patching" (orange) → patches
Unknown build greyed / disabled

State is re-read at click time so a stale label can't send the action the wrong way. Busy-state disabling is handled by the existing _set_busy path.

Verified headlessly across all four states (correct label/colour/action/enabled) plus busy-disable.

Stacked on top of #36 (base is feat/sideload-module) so both GUI changes can be tried in one run; GitHub will retarget this to master once #36 merges.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the BlueStacks engine patching UI in main.py by replacing the separate patch, restore, and status label elements with a single dynamic button that updates its label, color, and action based on the current patch state. Feedback suggests filtering out None values in _engine_state to prevent a single unrecognized installation from disabling the button for other valid installations, and using QPalette and setFont instead of setStyleSheet to preserve native button styling.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread main.py
Comment on lines 223 to +233
states = [integrity_patch.installation_patched(i["install_path"])
for i in self.installations
if i.get("patch_mode") and i.get("install_path")
and os.path.isdir(i["install_path"])]
if states and all(s is True for s in states):
return "Engine: Patched ✓ (applies to every instance)", "#2e7d32"
return "patched"
if states and all(s is False for s in states):
return "Engine: Not patched — click \"Patch BlueStacks Engine\" once", "#c62828"
return "unpatched"
if any(s is True for s in states):
return "Engine: Partially patched — re-run \"Patch BlueStacks Engine\"", "#e65100"
return "Engine: status unknown (unrecognized build?)", "#616161"
return "partial"
return "unknown"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If there are multiple installations and one of them is unrecognized or unreadable (returning None from installation_patched), the presence of that single None value will cause _engine_state to fall through and return "unknown". Because "unknown" disables the single engine button, this completely prevents the user from patching or restoring other valid, recognized installations.

Filtering out None values from states ensures that unrecognized builds do not block the management of valid installations.

Suggested change
states = [integrity_patch.installation_patched(i["install_path"])
for i in self.installations
if i.get("patch_mode") and i.get("install_path")
and os.path.isdir(i["install_path"])]
if states and all(s is True for s in states):
return "Engine: Patched ✓ (applies to every instance)", "#2e7d32"
return "patched"
if states and all(s is False for s in states):
return "Engine: Not patched — click \"Patch BlueStacks Engine\" once", "#c62828"
return "unpatched"
if any(s is True for s in states):
return "Engine: Partially patched — re-run \"Patch BlueStacks Engine\"", "#e65100"
return "Engine: status unknown (unrecognized build?)", "#616161"
return "partial"
return "unknown"
raw_states = [
integrity_patch.installation_patched(i["install_path"])
for i in self.installations
if i.get("patch_mode") and i.get("install_path")
and os.path.isdir(i["install_path"])
]
states = [s for s in raw_states if s is not None]
if states and all(s is True for s in states):
return "patched"
if states and all(s is False for s in states):
return "unpatched"
if any(s is True for s in states):
return "partial"
return "unknown"

Comment thread main.py
}[state]
self.engine_button.setText(text)
self.engine_button.setToolTip(tip)
self.engine_button.setStyleSheet(f"color: {color}; font-weight: bold;")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Applying a stylesheet via setStyleSheet on a QPushButton in PyQt5 often overrides the native platform styling (such as hover animations, rounded corners, and native borders on Windows).

To preserve the native OS look-and-feel of the button while still customizing its text color and making it bold, you can use QPalette and setFont instead.

Suggested change
self.engine_button.setStyleSheet(f"color: {color}; font-weight: bold;")
from PyQt5.QtGui import QPalette, QColor
palette = self.engine_button.palette()
palette.setColor(QPalette.ButtonText, QColor(color))
self.engine_button.setPalette(palette)
font = self.engine_button.font()
font.setBold(True)
self.engine_button.setFont(font)

@RobThePCGuy RobThePCGuy force-pushed the feat/engine-single-button branch 3 times, most recently from 6334f35 to 50a278f Compare July 9, 2026 04:30
Three separate widgets (Patch button, Undo button, status label) become a
single state-driven button:

- unpatched -> "Patch BlueStacks Engine (required for root)" (red), patches
- patched   -> "Engine patched - click to Undo" (green), restores
- partial   -> "Engine partially patched - click to finish patching" (orange)
- unknown   -> disabled, greyed

The label, colour, tooltip, and click action all derive from the live engine
state, re-read at click time so a stale label can't trigger the wrong action.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant