Skip to content
12 changes: 6 additions & 6 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ architecture, the modules, and the tests all refer to the same things.
query editor, and the last folder they exported to. Persisted between runs.
- **Zoom** — a scale factor the user applies to the text of a pane, in discrete
steps away from the size that pane was built with. Step 0 is the untouched
size. The query editor and the Tree each carry their own Zoom, so one can be
enlarged without the other. Part of the window state, so both are persisted
between runs.
size. There are two: one that the query editor and the result Messages pane
share, and one that the Tree carries, so either side can be enlarged without
the other. Part of the window state, so both are persisted between runs.

## Modules (current shape)

Expand All @@ -62,9 +62,9 @@ architecture, the modules, and the tests all refer to the same things.
progress to the GUI thread, surfaces cancel and completion.
- **ZoomPresenter** — owns one Zoom and applies it to the widgets registered
with it, turning the zoom gestures over those widgets into steps. MainWindow
keeps one per independently zoomable pane. Ctrl+wheel needs no routing
because a presenter only watches its own widgets; a keyboard zoom goes to
the presenter that holds the focus.
keeps one per independently zoomable group of panes. Ctrl+wheel needs no
routing because a presenter only watches its own widgets; a keyboard zoom
goes to the presenter that holds the focus.
- **SessionManager** — persists and restores the Session and window state.
- **MainWindow** — Qt shell. Wires the modules to menu actions and the UI
form. Does not contain business logic.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ making database operations seamless and straightforward.
- Export database schema as CREATE TABLE statements
- Export data as an SQL script containing INSERT statements or as CSV files
- Zoom the query editor and database tree independently, each with its own size,
using Ctrl +/- on the focused pane or Ctrl + mouse wheel over either one
using Ctrl +/- on the focused pane or Ctrl + mouse wheel over it. The query
result Messages pane zooms together with the query editor, and answers both
gestures itself
- Desktop color theme awareness with automatic switching between dark/light themes
- Command line interface for automation and scripting

Expand Down
6 changes: 5 additions & 1 deletion src/gui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ MainWindow::MainWindow(IDatabase *database, QWidget *parent) :
ui->setupUi(this);
ui->splitterMain->setStretchFactor(1, 3);
ui->splitterQueryTab->setStretchFactor(1, 1);
// Two presenters, so each pane carries its own zoom level.
// Two presenters, so the editor and the Tree zoom independently of one
// another. The messages pane rides on the editor's rather than carrying a
// third: it reports on the query the user is looking at, so the two read
// as one surface and stay at a single size.
this->editorZoom = std::make_unique<ZoomPresenter>(this);
this->editorZoom->addTarget(ui->textEdit);
this->editorZoom->addTarget(ui->queryResultMessagesTextEdit);
this->treeZoom = std::make_unique<ZoomPresenter>(this);
this->treeZoom->addTarget(ui->treeWidget);

Expand Down
52 changes: 52 additions & 0 deletions tests/test_mainwindow.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#include <gtest/gtest.h>
#include <QStandardPaths>
#include <QAction>
#include <QFontInfo>
#include <QPlainTextEdit>
#include <QTextEdit>
#include <QTreeWidget>
#include <memory>
Expand Down Expand Up @@ -50,6 +53,29 @@ class MainWindowTest : public ::testing::Test {
editor->setPlainText(sql);
}

// Zoom is driven the way the user drives it, through the menu actions,
// rather than by reaching for the presenters the window keeps private.
static void zoomIn(const MainWindow &window) {
auto *action = window.findChild<QAction *>("actionZoom_In");
ASSERT_NE(action, nullptr);
action->trigger();
}

static void resetZoom(const MainWindow &window) {
auto *action = window.findChild<QAction *>("actionReset_Zoom");
ASSERT_NE(action, nullptr);
action->trigger();
}

// The measure ZoomPresenter works in. A widget that inherits its font, or
// that was sized in pixels, reports no point size of its own, so reading
// pointSizeF() straight off it can yield -1 and turn a correct scaling
// into a failed comparison.
static double pointSize(const QWidget *widget) {
const double size = widget->font().pointSizeF();
return size > 0 ? size : QFontInfo(widget->font()).pointSizeF();
}

std::unique_ptr<InMemoryDatabase> db;
};

Expand Down Expand Up @@ -96,3 +122,29 @@ TEST_F(MainWindowTest, ACreateRefreshesTheTree) {

EXPECT_TRUE(tableNames(window).contains("made_by_the_window"));
}

// The messages pane rides on the editor's zoom, so one zoom moves both and
// they stay proportional to the sizes they were built with.
TEST_F(MainWindowTest, ZoomingTheEditorZoomsTheResultMessagesPane) {
const MainWindow window(db.get());
auto *editor = window.findChild<QTextEdit *>("textEdit");
auto *messages = window.findChild<QPlainTextEdit *>("queryResultMessagesTextEdit");
ASSERT_NE(editor, nullptr);
ASSERT_NE(messages, nullptr);

// The window restores whatever zoom the last run left behind, so start
// from a known step rather than from that.
resetZoom(window);
const double editorBefore = pointSize(editor);
const double messagesBefore = pointSize(messages);

zoomIn(window);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

EXPECT_GT(pointSize(editor), editorBefore);
EXPECT_NEAR(pointSize(messages),
messagesBefore * (pointSize(editor) / editorBefore), 0.001);

// The window persists its zoom step as it is torn down, so hand the next
// test the same starting point this one was given.
resetZoom(window);
}
Loading