qt: phase 1.3a — quickTerminal pointer + visibility/quickterm toggles

The s_quickTerminal pointer and the toggleVisibility /
toggleQuickTerminal entry points all move from MainWindow to the
GhosttyApp singleton.

  - s_quickTerminal → GhosttyApp::m_quickTerminal. The clearing on
    close now lives in GhosttyApp::unregisterWindow alongside the
    window list removal.
  - MainWindow::toggleVisibility → GhosttyApp::toggleVisibility
  - MainWindow::toggleQuickTerminal → GhosttyApp::toggleQuickTerminal,
    which calls a new MainWindow::makeQuickTerminal() factory for
    first-use construction (it owns the m_quickTerminal=true +
    setupLayerShell + animateIn dance).

Two callsites in MainWindow::onAction (TOGGLE_VISIBILITY,
TOGGLE_QUICK_TERMINAL) and two in main.cpp (the global-shortcut
handlers) updated to call the singleton.

New public accessor: MainWindow::isQuickTerminal() — read-only flag
so the singleton can identify the dropdown without poking the
private m_quickTerminal field. (Currently unused by the singleton
since unregisterWindow checks pointer equality, but kept since
phase 1.4+ will need it for the undoLastClose target filter.)

Build verification: not run on this Mac. Needs a docker compile
before merge.

Co-Authored-By: claude-flow <ruv@ruv.net>
pull/12846/head
ntomsic 2026-05-23 11:51:51 -05:00
parent 2dcfb63128
commit 7ea8c87a63
5 changed files with 59 additions and 46 deletions

View File

@ -97,7 +97,6 @@ ghostty_app_t MainWindow::s_app = nullptr;
ghostty_config_t MainWindow::s_config = nullptr;
bool MainWindow::s_needsPremultiply = false;
int MainWindow::s_quitDelayMs = 0;
MainWindow *MainWindow::s_quickTerminal = nullptr;
MainWindow::MainWindow() {
setWindowTitle(QStringLiteral("Ghastty"));
@ -158,8 +157,9 @@ MainWindow::MainWindow() {
}
MainWindow::~MainWindow() {
// unregisterWindow also clears GhosttyApp's quick-terminal pointer
// if this was it.
GhosttyApp::instance().unregisterWindow(this);
if (this == s_quickTerminal) s_quickTerminal = nullptr;
// Destroy this window's surfaces (freeing their ghostty_surface_t)
// before any app teardown; Qt's own child cleanup runs after this body.
@ -957,46 +957,17 @@ void MainWindow::closeAllWindows(bool thenQuit) {
}
}
void MainWindow::toggleVisibility() {
// If anything is showing, hide everything; otherwise reveal it all.
const QList<MainWindow *> &live = GhosttyApp::instance().windows();
bool anyVisible = false;
for (MainWindow *w : live)
if (w->isVisible()) {
anyVisible = true;
break;
}
for (MainWindow *w : live) {
if (anyVisible) {
w->hide();
} else {
w->show();
w->raise();
w->activateWindow();
}
}
}
void MainWindow::toggleQuickTerminal() {
if (s_quickTerminal) {
if (s_quickTerminal->isVisible()) {
s_quickTerminal->animateQuickTerminalOut();
} else {
s_quickTerminal->animateQuickTerminalIn();
}
return;
}
// First use: build the dedicated quick-terminal window.
MainWindow *MainWindow::makeQuickTerminal() {
auto *w = new MainWindow;
w->m_quickTerminal = true;
w->setAttribute(Qt::WA_DeleteOnClose);
if (!w->initialize()) {
delete w;
return;
return nullptr;
}
s_quickTerminal = w;
w->setupLayerShell();
w->animateQuickTerminalIn();
return w;
}
// Read quick-terminal-animation-duration (seconds) and convert to ms.
@ -2520,11 +2491,11 @@ bool MainWindow::onAction(ghostty_app_t, ghostty_target_s target,
}
case GHOSTTY_ACTION_TOGGLE_VISIBILITY:
post(qApp, []() { MainWindow::toggleVisibility(); });
post(qApp, []() { GhosttyApp::instance().toggleVisibility(); });
return true;
case GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL:
post(qApp, []() { MainWindow::toggleQuickTerminal(); });
post(qApp, []() { GhosttyApp::instance().toggleQuickTerminal(); });
return true;
case GHOSTTY_ACTION_TOGGLE_COMMAND_PALETTE:

View File

@ -43,18 +43,18 @@ public:
// tab whose surface inherits from `parent` (may be null).
static MainWindow *newWindow(ghostty_surface_t parent);
// Show or hide every window at once (TOGGLE_VISIBILITY).
static void toggleVisibility();
// Show/hide the dropdown quick terminal, creating it on first use
// (TOGGLE_QUICK_TERMINAL). There is at most one per process.
static void toggleQuickTerminal();
// Build the process's single quick-terminal MainWindow on demand:
// a layer-shell dropdown anchored to a screen edge, faded in
// immediately. Called from GhosttyApp::toggleQuickTerminal on first
// use. Returns nullptr on init failure.
static MainWindow *makeQuickTerminal();
// Quick-terminal slide/fade animation per quick-terminal-animation-
// duration. Implemented as a windowOpacity fade because Qt's layer-
// shell doesn't expose a usable position-based slide API.
void animateQuickTerminalIn();
void animateQuickTerminalOut();
bool isQuickTerminal() const { return m_quickTerminal; }
// Open a new tab. `parent` (may be null) is the surface whose working
// directory etc. the new surface should inherit.
@ -289,7 +289,6 @@ private:
// Mirror of GhosttyApp::quitDelayMs; phase 1.3 retires it when the
// remaining call site (closeAllWindows) moves to the singleton.
static int s_quitDelayMs; // 0 = no delay configured
static MainWindow *s_quickTerminal; // the one quick terminal, if any
// Snapshot of a closed tab or window for undo/redo. `pageTitles`
// holds each tab's last-known title (window snapshots have N tabs;

View File

@ -108,6 +108,41 @@ void GhosttyApp::registerWindow(MainWindow *w) {
void GhosttyApp::unregisterWindow(MainWindow *w) {
m_windows.removeOne(w);
if (m_quickTerminal == w) m_quickTerminal = nullptr;
}
void GhosttyApp::toggleVisibility() {
// If anything is showing, hide everything; otherwise reveal it all.
bool anyVisible = false;
for (MainWindow *w : m_windows)
if (w->isVisible()) {
anyVisible = true;
break;
}
for (MainWindow *w : m_windows) {
if (anyVisible) {
w->hide();
} else {
w->show();
w->raise();
w->activateWindow();
}
}
}
void GhosttyApp::toggleQuickTerminal() {
if (m_quickTerminal) {
if (m_quickTerminal->isVisible())
m_quickTerminal->animateQuickTerminalOut();
else
m_quickTerminal->animateQuickTerminalIn();
return;
}
// First use: build the dedicated quick-terminal window. It registers
// itself via the standard registerWindow path; we additionally
// remember it as the singleton dropdown so a second toggle-call
// animates rather than building another window.
m_quickTerminal = MainWindow::makeQuickTerminal();
}
void GhosttyApp::ensureFrameTimer() {

View File

@ -66,7 +66,14 @@ public:
// per process. Owned by Qt (WA_DeleteOnClose); GhosttyApp holds a
// non-owning pointer so toggleQuickTerminal can find it.
MainWindow *quickTerminal() const { return m_quickTerminal; }
void setQuickTerminal(MainWindow *w) { m_quickTerminal = w; }
// App-scoped show/hide of every regular window. Replaces
// MainWindow::toggleVisibility().
void toggleVisibility();
// Show/hide the dropdown, creating it on first use. Replaces
// MainWindow::toggleQuickTerminal().
void toggleQuickTerminal();
// ---- frame + quit timers ----------------------------------------

View File

@ -5,6 +5,7 @@
#include <QIcon>
#include <QSurfaceFormat>
#include "app/GhosttyApp.h"
#include "GlobalShortcuts.h"
#include "MainWindow.h"
#include "ghostty.h"
@ -121,9 +122,9 @@ int main(int argc, char **argv) {
QObject::connect(&globalShortcuts, &GlobalShortcuts::activated,
[](const QString &id) {
if (id == QLatin1String("toggle-quick-terminal"))
MainWindow::toggleQuickTerminal();
GhosttyApp::instance().toggleQuickTerminal();
else if (id == QLatin1String("toggle-visibility"))
MainWindow::toggleVisibility();
GhosttyApp::instance().toggleVisibility();
});
return app.exec();