View › Grid Settings…: spacing, subdivisions, colour, style and opacity, kept across launches (13.5b)

This commit is contained in:
ZacharyZhang-NY committed 2026-10-01 12:43:50 -04:00
1 parent 3992bc913d
commit 93a4c62b8f
29 files changed
+1353 -46

No files matched your search

+149 -8
View File
@@ -1,5 +1,6 @@
#include "Document/Guides.h"
#include "Document/EditorSession.h"
#include "Logging.h"
#include <algorithm>
#include <cmath>
@@ -39,19 +40,159 @@ std::optional<CanvasGuide::Axis> guideAxis(const QString &text)
return std::nullopt;
}
std::vector<double> LayoutGrid::lines(double length)
LayoutGrid::LayoutGrid(int spacing, int subdivisions)
: spacing(std::clamp(spacing, spacingLow, spacingHigh)), subdivisions(std::min({std::max(subdivisions, subdivisionLow), subdivisionHigh, this->spacing}))
{
}
// Counted, not summed: uneven steps stay on the majors.
std::vector<double> LayoutGrid::lines(double length) const
{
if (!(length >= 0))
return {0};
const int count = int(std::floor(length / step() + 0.001));
std::vector<double> result;
for (double value = 0; value <= length + 0.001; value += step)
result.push_back(std::round(value));
for (int index = 0; index <= count; ++index)
result.push_back(std::round(index * step()));
return result;
}
bool LayoutGrid::isMajor(double value)
bool LayoutGrid::isMajor(double value) const
{
return std::abs(std::fmod(std::round(value), spacing)) < 0.001;
return std::abs(std::fmod(std::round(value), double(spacing))) < 0.001;
}
LayoutGrid LayoutGrid::stored()
{
return LayoutGrid(ToolDefaults::integer(QStringLiteral("gridSpacing"), 64), ToolDefaults::integer(QStringLiteral("gridSubdivisions"), 8));
}
void LayoutGrid::store() const
{
ToolDefaults::set(spacing, QStringLiteral("gridSpacing"));
ToolDefaults::set(subdivisions, QStringLiteral("gridSubdivisions"));
}
QString rawValue(GridAppearance::Preset preset)
{
using Preset = GridAppearance::Preset;
switch (preset) {
case Preset::lightGray: return QStringLiteral("Light Gray");
case Preset::lightBlue: return QStringLiteral("Light Blue");
case Preset::lightRed: return QStringLiteral("Light Red");
case Preset::green: return QStringLiteral("Green");
case Preset::mediumBlue: return QStringLiteral("Medium Blue");
case Preset::yellow: return QStringLiteral("Yellow");
case Preset::magenta: return QStringLiteral("Magenta");
case Preset::cyan: return QStringLiteral("Cyan");
case Preset::black: return QStringLiteral("Black");
case Preset::custom: return QStringLiteral("Custom");
}
throw std::logic_error("unknown grid preset");
}
QString rawValue(GridAppearance::Style style)
{
switch (style) {
case GridAppearance::Style::lines: return QStringLiteral("Lines");
case GridAppearance::Style::dashedLines: return QStringLiteral("Dashed Lines");
case GridAppearance::Style::dots: return QStringLiteral("Dots");
}
throw std::logic_error("unknown grid style");
}
std::optional<PaletteColor> presetColor(GridAppearance::Preset preset)
{
using Preset = GridAppearance::Preset;
switch (preset) {
case Preset::lightGray: return PaletteColor{0.7, 0.7, 0.7};
case Preset::lightBlue: return PaletteColor{0.29, 0.78, 1};
case Preset::lightRed: return PaletteColor{1, 0.4, 0.4};
case Preset::green: return PaletteColor{0.25, 0.8, 0.25};
case Preset::mediumBlue: return PaletteColor{0.2, 0.4, 1};
case Preset::yellow: return PaletteColor{1, 1, 0};
case Preset::magenta: return PaletteColor{1, 0, 1};
case Preset::cyan: return PaletteColor{0, 1, 1};
case Preset::black: return PaletteColor::black();
case Preset::custom: return std::nullopt;
}
throw std::logic_error("unknown grid preset");
}
std::vector<double> dashes(GridAppearance::Style style)
{
switch (style) {
case GridAppearance::Style::lines: return {};
case GridAppearance::Style::dashedLines: return {4, 3};
case GridAppearance::Style::dots: return {1, 2};
}
throw std::logic_error("unknown grid style");
}
PaletteColor GridAppearance::color() const
{
return presetColor(preset).value_or(customColor);
}
// A grid that is on never disappears: 1% at least.
double GridAppearance::majorAlpha() const
{
return std::clamp(opacity, opacityLow, opacityHigh) / 100.0;
}
namespace {
// A stored name, else the default and a warning.
template <typename Value, typename Values>
Value named(const QString &key, const Values &values, Value fallback)
{
const QString stored = ToolDefaults::text(key);
for (const Value value : values)
if (rawValue(value) == stored)
return value;
if (!stored.isEmpty())
qCWarning(lcApp).noquote() << "ignoring the tool setting" << key << "of" << stored;
return fallback;
}
}
GridAppearance GridAppearance::stored()
{
GridAppearance appearance;
appearance.preset = named(QStringLiteral("gridColor"), allGridPresets, appearance.preset);
const QString custom = ToolDefaults::text(QStringLiteral("gridCustomColor"));
if (const std::optional<PaletteColor> color = PaletteColor::fromHex(custom))
appearance.customColor = *color;
else if (!custom.isEmpty())
qCWarning(lcApp).noquote() << "ignoring the tool setting gridCustomColor of" << custom;
appearance.style = named(QStringLiteral("gridStyle"), allGridStyles, appearance.style);
appearance.opacity = ToolDefaults::integer(QStringLiteral("gridOpacity"), appearance.opacity);
return appearance;
}
void GridAppearance::store() const
{
ToolDefaults::set(rawValue(preset), QStringLiteral("gridColor"));
ToolDefaults::set(customColor.hex(), QStringLiteral("gridCustomColor"));
ToolDefaults::set(rawValue(style), QStringLiteral("gridStyle"));
ToolDefaults::set(opacity, QStringLiteral("gridOpacity"));
}
void EditorSession::setLayoutGrid(const LayoutGrid &grid)
{
if (grid == m_layoutGrid)
return;
m_layoutGrid = grid;
grid.store();
notify();
}
void EditorSession::setGridAppearance(const GridAppearance &appearance)
{
if (appearance == m_gridAppearance)
return;
m_gridAppearance = appearance;
appearance.store();
notify();
}
void EditorSession::setShowsGrid(bool shows)
@@ -274,9 +415,9 @@ SnapGuides EditorSession::alignmentSnapTargets(const QSet<QUuid> &moving, bool i
}
// Hidden extras do not snap, as in Photoshop.
if (m_snapToGrid && m_showsGrid) {
for (double x : LayoutGrid::lines(size.width()))
for (double x : m_layoutGrid.lines(size.width()))
targets.xs.push_back(x);
for (double y : LayoutGrid::lines(size.height()))
for (double y : m_layoutGrid.lines(size.height()))
targets.ys.push_back(y);
}
if (m_snapToGuides && m_showsGuides) {
@@ -295,7 +436,7 @@ double EditorSession::snappedGuidePosition(double position, CanvasGuide::Axis ax
const double length = vertical ? m_document->size().width() : m_document->size().height();
std::vector<double> targets;
if (m_snapToGrid && m_showsGrid)
targets = LayoutGrid::lines(length);
targets = m_layoutGrid.lines(length);
if (m_snapToGuides && m_showsGuides) {
for (const CanvasGuide &guide : displayedGuides()) {
if (guide.axis == axis && guide.id != excluding)