14.3 round 1: print sizes tie to even; one trim of Foundation's .whitespaces, U+200B included, for New Canvas, validDimension and hex

This commit is contained in:
ZacharyZhang-NY committed 2026-10-11 03:12:40 -04:00
1 parent f8c1332c29
commit bb9b5c959e
8 files changed
+38 -26

No files matched your search

+2 -8
View File
@@ -20,14 +20,8 @@ QString PaletteColor::hex() const
std::optional<PaletteColor> PaletteColor::fromHex(const QString &hex)
{
// Swift trims spaces and tabs, not line breaks.
const auto blank = [](QChar character) { return character.category() == QChar::Separator_Space || character == u'\t'; };
qsizetype start = 0, end = hex.size();
while (start < end && blank(hex.at(start)))
++start;
while (end > start && blank(hex.at(end - 1)))
--end;
QString text = hex.mid(start, end - start);
// Swift trims .whitespaces, never line breaks.
QString text = trimmedWhitespaces(hex);
if (text.startsWith(u'#'))
text.remove(0, 1);
if (text.size() == 3)
+12 -7
View File
@@ -59,7 +59,7 @@ namespace {
// Swift's CharacterSet.whitespaces: category Zs and tab.
bool isWhitespace(QChar character)
{
return character == u'\t' || character.category() == QChar::Separator_Space;
return character == u'\t' || character == QChar(0x200B) || character.category() == QChar::Separator_Space;
}
// Swift's Int(String): optional sign, then ASCII digits only.
@@ -78,14 +78,19 @@ std::optional<int> wholeNumber(QStringView text)
}
}
QString trimmedWhitespaces(const QString &text)
{
qsizetype first = 0, last = text.size();
while (first < last && isWhitespace(text[first]))
++first;
while (last > first && isWhitespace(text[last - 1]))
--last;
return text.mid(first, last - first);
}
std::optional<int> CanvasDocument::validDimension(const QString &value)
{
qsizetype first = 0, last = value.size();
while (first < last && isWhitespace(value[first]))
++first;
while (last > first && isWhitespace(value[last - 1]))
--last;
const std::optional<int> number = wholeNumber(QStringView(value).mid(first, last - first));
const std::optional<int> number = wholeNumber(trimmedWhitespaces(value));
if (!number || *number < 1 || *number > DocumentLimits::maxSide)
return std::nullopt;
return number;
+2
View File
@@ -90,5 +90,7 @@ enum class NavigationTool {
// The rail's tooltip: the tool's name and its key.
QString label(NavigationTool tool);
// Swift's trim of .whitespaces: tab, space separators, U+200B.
QString trimmedWhitespaces(const QString &text);
bool isBrushTool(NavigationTool tool);
bool isSelectionTool(NavigationTool tool);
+10 -9
View File
@@ -15,6 +15,7 @@
#include <cctype>
#include <clocale>
#include <cmath>
#include <cstdio>
#include <cstdlib>
namespace {
@@ -134,13 +135,8 @@ NewCanvasUnit next(NewCanvasUnit unit)
std::optional<int> pixels(NewCanvasUnit unit, const QString &text, double resolution)
{
QString trimmed = text;
// Spaces and tabs trimmed; a comma is the point.
while (!trimmed.isEmpty() && (trimmed.front() == u'\t' || trimmed.front().category() == QChar::Separator_Space))
trimmed.remove(0, 1);
while (!trimmed.isEmpty() && (trimmed.back() == u'\t' || trimmed.back().category() == QChar::Separator_Space))
trimmed.chop(1);
const std::optional<double> value = number(trimmed.replace(u',', u'.'));
// A comma is the decimal point.
const std::optional<double> value = number(trimmedWhitespaces(text).replace(u',', u'.'));
if (!value || *value <= 0)
return std::nullopt;
const std::optional<double> inch = perInch(unit);
@@ -155,8 +151,13 @@ QString text(NewCanvasUnit unit, int pixels, double resolution)
const std::optional<double> inch = perInch(unit);
if (!inch)
return QString::number(pixels);
// Two decimals at most, none trailing, never grouped.
QString written = QString::number(double(pixels) / resolution * *inch, 'f', 2);
// glibc's halves go to even, as Foundation's; Qt's do not.
static const locale_t c = newlocale(LC_ALL_MASK, "C", locale_t(nullptr));
const locale_t previous = uselocale(c);
char digits[64];
std::snprintf(digits, sizeof digits, "%.2f", double(pixels) / resolution * *inch);
uselocale(previous);
QString written = QString::fromLatin1(digits);
while (written.endsWith(u'0'))
written.chop(1);
if (written.endsWith(u'.'))