14.7: Canvas Only, Toggle Fullscreen on F and Escape

This commit is contained in:
ZacharyZhang-NY committed 2026-10-11 05:38:35 -04:00
1 parent 56f444b1ad
commit e1207281a4
19 files changed
+416 -16

No files matched your search

+10 -2
View File
@@ -2,6 +2,7 @@
#include "IO/RecentProjects.h"
#include "Logging.h"
#include "UI/KeyboardShortcuts.h"
#include "UI/ProjectWorkspaceView.h"
#include <QApplication>
#include <QMenu>
@@ -22,8 +23,8 @@ QString filterName(FilterKind kind)
}
}
CompositorMenus::CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, QWidget &window)
: QObject(&bar), m_workspace(workspace), m_window(window)
CompositorMenus::CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, ProjectWorkspaceView &window)
: QObject(&bar), m_workspace(workspace), m_window(window), m_view(window)
{
QMenu *file = bar.addMenu(QStringLiteral("&File"));
add(file, QStringLiteral("newCanvas"), QStringLiteral("New Canvas…"), QKeySequence(Qt::CTRL | Qt::Key_N), [this] { projects().newCanvas(); });
@@ -126,6 +127,9 @@ CompositorMenus::CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, QWi
QMenu *view = bar.addMenu(QStringLiteral("&View"));
add(view, QStringLiteral("searchCommands"), QStringLiteral("Search Commands…"), QKeySequence(Qt::CTRL | Qt::Key_F),
[this, &bar] { m_palette.toggle(session(), bar, action(QStringLiteral("searchCommands"))); });
// A plain F; a field takes its letters first.
add(view, QStringLiteral("toggleFullscreen"), QStringLiteral("Toggle Fullscreen"), QKeySequence(Qt::Key_F), [this] { m_view.toggleCanvasOnly(); })
->setCheckable(true);
view->addSeparator();
add(view, QStringLiteral("fit"), QStringLiteral("Fit Canvas"), QKeySequence(Qt::CTRL | Qt::Key_0), [this] { session().fit(); });
add(view, QStringLiteral("actualPixels"), QStringLiteral("Actual Pixels"), QKeySequence(Qt::CTRL | Qt::Key_1), [this] { session().zoom(1); });
@@ -291,6 +295,8 @@ QAction *CompositorMenus::add(QMenu *menu, const QString &name, const QString &t
made->setObjectName(name);
made->setProperty("originalShortcut", shortcut);
made->setShortcut(ShortcutSettings::shared().menu(shortcut));
// Keys still work while Canvas Only hides the bar.
m_window.addAction(made);
connect(made, &QAction::triggered, this, run);
return made;
}
@@ -357,6 +363,8 @@ void CompositorMenus::synchronize()
for (const char *name : {"fit", "actualPixels", "zoomIn", "zoomOut"})
action(QString::fromLatin1(name))->setEnabled(drawn);
action(QStringLiteral("pixelGrid"))->setChecked(s.showsPixelGrid());
action(QStringLiteral("toggleFullscreen"))->setChecked(s.canvasOnly());
action(QStringLiteral("toggleFullscreen"))->setEnabled(s.canToggleCanvasOnly());
const bool opened = s.document().has_value();
for (const auto &[name, value] : std::initializer_list<std::pair<const char *, bool>>{
{"showGrid", s.showsGrid()}, {"showGuides", s.showsGuides()}, {"showRulers", s.showsRulers()}, {"snapEnabled", s.snapEnabled()},
+4 -1
View File
@@ -9,11 +9,13 @@
#include <QMenuBar>
#include <QPointer>
class ProjectWorkspaceView;
// The menu bar: Swift's commands whose session functions exist.
class CompositorMenus : public QObject {
Q_OBJECT
public:
CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, QWidget &window);
CompositorMenus(ProjectWorkspace &workspace, QMenuBar &bar, ProjectWorkspaceView &window);
// The panel's hide moves the focus: stop listening first.
~CompositorMenus() override { disconnect(qApp, nullptr, this, nullptr); }
@@ -35,6 +37,7 @@ private:
ProjectWorkspace &m_workspace;
QWidget &m_window;
ProjectWorkspaceView &m_view;
FloatingPanel m_shortcutsPanel{QStringLiteral("keyboardShortcuts"), m_window};
CommandPaletteController m_palette{m_window};
QMetaObject::Connection m_sessionWatch;
+1 -1
View File
@@ -138,7 +138,7 @@ const std::vector<ShortcutDefinition> &ShortcutDefinition::all()
entry("Move Layer Down", "[", 1, true), entry("Merge Layers", "e", 1, true), entry("Show Grid", "'", 1, true),
entry("Show Guides", ";", 1, true), entry("Show Rulers", "r", 1, true), entry("Snap", ";", 9, true), entry("Lock Guides", ";", 3, true)};
const std::vector<std::pair<const char *, QString>> tools{
{"Select tool", "a"}, {"Move / Transform tool", "v"}, {"Hand tool", "h"}, {"Zoom tool", "z"}, {"Brush tool", "b"},
{"Toggle Fullscreen: the canvas alone on black (Esc also leaves)", "f"}, {"Select tool", "a"}, {"Move / Transform tool", "v"}, {"Hand tool", "h"}, {"Zoom tool", "z"}, {"Brush tool", "b"},
{"Eraser", "e"}, {"Spot Healing", "j"}, {"Clone Stamp", "s"}, {"Type tool", "t"}, {"Gradient tool", "g"},
{"Shape tool", "u"}, {"Eyedropper tool", "i"}, {"Marquee / cycle shape", "m"}, {"Magic", "w"},
{"Lasso / cycle mode", "l"}, {"Blur / Smudge / Liquify", "r"}, {"Crop tool", "c"}, {"Swap foreground/background", "x"},
+48
View File
@@ -1,5 +1,11 @@
#include "UI/ProjectWorkspaceView.h"
#include "Logging.h"
#include <QApplication>
#include <QCloseEvent>
#include <QDialog>
#include <QKeyEvent>
#include <QLineEdit>
#include <QMenuBar>
namespace {
QAction *action(QToolBar *bar, const QString &text, const QString &help, const QString &name)
@@ -41,6 +47,7 @@ ProjectWorkspaceView::ProjectWorkspaceView(ProjectWorkspace &workspace, QWidget
connect(m_zoomIn, &QAction::triggered, this, [this] { m_workspace.current().session.zoomKeyboard(1); });
connect(m_zoomOut, &QAction::triggered, this, [this] { m_workspace.current().session.zoomKeyboard(-1); });
connect(&m_workspace, &ProjectWorkspace::changed, this, &ProjectWorkspaceView::synchronize);
qApp->installEventFilter(this);
synchronize();
}
@@ -96,6 +103,47 @@ void ProjectWorkspaceView::synchronizeSession()
setWindowFilePath(session.projectPath().value_or(QString()));
}
void ProjectWorkspaceView::toggleCanvasOnly()
{
EditorSession &session = m_workspace.current().session;
if (!session.canvasOnly()) {
// Already fullscreen, it has the screen: only bars go.
if (!windowState().testFlag(Qt::WindowFullScreen)) {
m_beforeCanvasOnly = windowState();
menuBar()->hide();
setWindowState(windowState() | Qt::WindowFullScreen);
}
m_toolbar->hide();
session.setCanvasOnly(true);
} else {
session.setCanvasOnly(false);
if (m_beforeCanvasOnly) {
menuBar()->show();
setWindowState(m_beforeCanvasOnly.value());
m_beforeCanvasOnly.reset();
}
m_toolbar->show();
}
qCInfo(lcApp) << "canvas only" << session.canvasOnly();
}
bool ProjectWorkspaceView::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() != QEvent::KeyPress || static_cast<QKeyEvent *>(event)->key() != Qt::Key_Escape)
return QMainWindow::eventFilter(watched, event);
const Qt::KeyboardModifiers held = static_cast<QKeyEvent *>(event)->modifiers() & (Qt::ControlModifier | Qt::MetaModifier | Qt::AltModifier | Qt::ShiftModifier);
const EditorSession &session = m_workspace.current().session;
QWidget *focus = QApplication::focusWidget();
// A field's Escape is its own, and a docked panel's.
bool typing = qobject_cast<QLineEdit *>(focus) != nullptr;
for (QWidget *each = focus; each && !typing; each = each->parentWidget())
typing = qobject_cast<QDialog *>(each) != nullptr;
if (held || !session.canvasOnly() || !session.escapeHasNothingToCancel() || QApplication::activeWindow() != this || typing)
return QMainWindow::eventFilter(watched, event);
toggleCanvasOnly();
return true;
}
void ProjectWorkspaceView::closeEvent(QCloseEvent *event)
{
// The workspace asks first, then closes us with its mark.
+6
View File
@@ -15,9 +15,13 @@ public:
ContentView *content() const { return m_content; }
ProjectTabStrip *tabs() const { return m_tabs; }
// Canvas Only: the window fills the screen, bars away.
void toggleCanvasOnly();
protected:
void closeEvent(QCloseEvent *event) override;
// Escape leaves fullscreen once nothing is left to cancel.
bool eventFilter(QObject *watched, QEvent *event) override;
private:
void synchronize();
@@ -35,5 +39,7 @@ private:
ContentView *m_content = nullptr;
// Held until its editor goes, as Swift's views hold sessions.
std::shared_ptr<ProjectTab> m_shownTab;
// The window's state before Canvas Only, to put back.
std::optional<Qt::WindowStates> m_beforeCanvasOnly;
QMetaObject::Connection m_sessionWatch;
};