diff --git a/scribus/numeration.h b/scribus/numeration.h index 88857e042..a3bad3aed 100644 --- a/scribus/numeration.h +++ b/scribus/numeration.h @@ -77,6 +77,7 @@ struct NumStruct QString m_name; QList m_nums; QList m_counters; + QList m_maxCounters; // highest value m_counters ever reached, per level int m_lastlevel { -1 }; }; diff --git a/scribus/pageitem_textframe.cpp b/scribus/pageitem_textframe.cpp index 1c5b95c58..ed0eb1df3 100644 --- a/scribus/pageitem_textframe.cpp +++ b/scribus/pageitem_textframe.cpp @@ -1075,6 +1075,152 @@ double calculateLineSpacing (const ParagraphStyle &style, PageItem *item) return style.lineSpacing(); } +static QString numListKey(const ParagraphStyle& style) +{ + return style.numName() + QLatin1Char('\x1f') + + QString::number(style.numFormat()) + QLatin1Char('\x1f') + + style.numPrefix() + QLatin1Char('\x1f') + + style.numSuffix() + QLatin1Char('\x1f') + + QString::number(style.numLevel()); +} +// Build the text a given count would produce for this list level. +// Pure string formatting — no document access, no shaping. +static QString candidateMarkerString(const ParagraphStyle& style, int count) +{ + Numeration num; + num.numFormat = static_cast(style.numFormat()); + QString result = style.numPrefix(); + result += num.numString(count); + result += style.numSuffix(); + return result; +} + +// Shape one or more short candidate strings through the real font/kerning +// path and return the widest rendered result. The candidates are synthetic +// (not real document text), so this never depends on itemText or frame chains. +static double shapeCandidateWidths(PageItem* item, const ParagraphStyle& style, const QStringList& candidates) +{ + StoryText probe(item->doc()); + for (const QString& candidate : candidates) + { + if (probe.length() > 0) + probe.insertChars(SpecialChars::PARSEP); + int pos = probe.length(); + probe.insertChars(candidate); + probe.applyStyle(pos, style); + probe.applyCharStyle(pos, candidate.length(), style.charStyle()); + } + + QList glyphClusters; + ShapedTextFeed feed(&probe, 0, item); + for (int i = 0; feed.haveMoreText(i, glyphClusters); ++i) + ; + + double maxWidth = 0.0, current = 0.0; + int prevA = -1; + for (const auto& glyph : as_const(glyphClusters)) + { + int a = glyph.firstChar(); + if (probe.isBlockStart(a) && a != prevA) + { + prevA = a; + maxWidth = qMax(maxWidth, current); + current = 0.0; + } + current += glyph.width(); + } + return qMax(maxWidth, current); +} +static double maxParagraphEffectWidthLegacyScan(PageItem* item, const QString& targetListKey) +{ + // Fallback only: used when counter data isn't available yet + // (e.g. numbering hasn't run for this list this pass). + ShapedTextFeed shapedText(&item->itemText, 0, item); + + QList glyphClusters; + for (int j = 0; shapedText.haveMoreText(j, glyphClusters); ++j) + ; + + double maxParEffectWidth = 0.0; + int prevA = -1; + + for (int j = 0; j < glyphClusters.count(); ++j) + { + int a = glyphClusters[j].firstChar(); + if (item->itemText.isBlockStart(a) && a != prevA) + { + prevA = a; + const ParagraphStyle& pStyle = item->itemText.paragraphStyle(a); + if (pStyle.hasNum() && numListKey(pStyle) == targetListKey) + { + double effectWidth = 0.0; + for (int k = j; k < glyphClusters.count(); ++k) + { + const auto& glyph = glyphClusters[k]; + if (glyph.firstChar() != a) + break; + effectWidth += glyph.width(); + } + + double totalWidth = pStyle.parEffectOffset() + effectWidth; + if (totalWidth > maxParEffectWidth) + maxParEffectWidth = totalWidth; + } + } + } + return maxParEffectWidth; +} + +static double maxParagraphEffectWidth(PageItem* item, const QString& targetListKey, const ParagraphStyle& style) +{ + QHash& sharedCache = item->itemText.maxParEffectWidthCache(); + auto cacheIt = sharedCache.constFind(targetListKey); + if (cacheIt != sharedCache.constEnd()) + return cacheIt.value(); + + ScribusDoc* doc = item->doc(); + NumStruct* numS = doc ? doc->numerations.value(style.numName(), nullptr) : nullptr; + int level = style.numLevel(); + + double result; + if (!numS || level < 0 || level >= numS->m_maxCounters.count()) + { + result = maxParagraphEffectWidthLegacyScan(item, targetListKey); + } + else + { + int maxCount = qMax(1, numS->m_maxCounters.at(level)); + auto fmt = static_cast(style.numFormat()); + + QStringList candidates; + if (fmt == Type_1_2_3 || fmt == Type_1_2_3_ar) + { + candidates << candidateMarkerString(style, maxCount); + } + else + { + int longestLen = -1; + for (int n = 1; n <= maxCount; ++n) + { + QString candidate = candidateMarkerString(style, n); + if (candidate.length() > longestLen) + { + longestLen = candidate.length(); + candidates.clear(); + candidates << candidate; + } + else if (candidate.length() == longestLen) + { + candidates << candidate; + } + } + } + result = style.parEffectOffset() + shapeCandidateWidths(item, style, candidates); + } + + sharedCache.insert(targetListKey, result); + return result; +} // This assumes that layout() ran on the previous page and set the incomplete* vars // It also clears the incomplete* vars, and changes the starting position for this frame @@ -1391,8 +1537,8 @@ void PageItem_TextFrame::layout() setMaxY(-1); double maxYAsc = 0.0, maxYDesc = 0.0; int regionMinY = 0, regionMaxY= 0; + QHash cachedMaxParEffectWidthByList; - double autoLeftIndent = 0.0; for (int i = 0; shapedText.haveMoreText(i, glyphClusters); ++i) { int currentIndex = i - current.lineData.firstCluster; @@ -1425,16 +1571,6 @@ void PageItem_TextFrame::layout() BulNumMode = false; if (itemText.isBlockStart(a)) { - if (currentIndex > 0) - { - int prevA = current.glyphs[currentIndex - 1].firstChar(); - if (a != prevA) - autoLeftIndent = 0.0; - } - else - { - autoLeftIndent = 0.0; - } style = itemText.paragraphStyle(a); if (style.hasBullet() || style.hasNum()) { @@ -1807,7 +1943,7 @@ void PageItem_TextFrame::layout() else { // LTR: Original behavior - current.leftIndent = style.leftMargin() + autoLeftIndent; + current.leftIndent = style.leftMargin(); if (itemText.isBlockStart(a)) { current.leftIndent += style.firstIndent(); @@ -1816,33 +1952,34 @@ void PageItem_TextFrame::layout() if (BulNumMode || DropCmode) { - if (style.parEffectIndent()) + double effectWidth = 0.0; + for (int j = i; shapedText.haveMoreText(j, glyphClusters); ++j) { - double effectWidth = 0.0; - for (int j = i; shapedText.haveMoreText(j, glyphClusters); ++j) - { - const auto & glyph = glyphClusters[j]; - if (glyph.firstChar() != a) - break; - effectWidth += glyph.width(); - } + const auto& glyph = glyphClusters[j]; + if (glyph.firstChar() != a) + break; + effectWidth += glyph.width(); + } + double indentAdjust = 0.0; + if (style.suffixAlignment() == ParagraphStyle::SuffixAlign_Right || + style.suffixAlignment() == ParagraphStyle::SuffixAlign_Center) + { + const QString listKey = numListKey(style); + if (!cachedMaxParEffectWidthByList.contains(listKey)) + cachedMaxParEffectWidthByList[listKey] = maxParagraphEffectWidth(this, listKey,style); - if (style.direction() == ParagraphStyle::RTL) - { - current.rightIndent -= style.parEffectOffset() + effectWidth; - if (current.rightIndent < 0.0) - current.rightIndent = 0.0; - } + double maxParEffectWidth = cachedMaxParEffectWidthByList.value(listKey); + + if (style.suffixAlignment() == ParagraphStyle::SuffixAlign_Right) + indentAdjust = style.parEffectOffset() + (effectWidth - maxParEffectWidth); else - { - current.leftIndent -= style.parEffectOffset() + effectWidth; - if (current.leftIndent < 0.0) - { - autoLeftIndent = abs(current.leftIndent); - current.leftIndent = 0.0; - } - } + indentAdjust = style.parEffectOffset() + (effectWidth - maxParEffectWidth) / 2.0; } + + if (style.direction() == ParagraphStyle::RTL) + current.rightIndent -= indentAdjust; + else + current.leftIndent -= indentAdjust; } // RTL drop-cap follow-lines: Constrain the available line width // from the right margin to prevent text overlapping the right-aligned drop cap. diff --git a/scribus/plugins/fileloader/scribus150format/scribus150format.cpp b/scribus/plugins/fileloader/scribus150format/scribus150format.cpp index 424ddcfbc..763a573ba 100644 --- a/scribus/plugins/fileloader/scribus150format/scribus150format.cpp +++ b/scribus/plugins/fileloader/scribus150format/scribus150format.cpp @@ -3123,10 +3123,6 @@ void Scribus150Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re if (attrs.hasAttribute(ParagraphEffectOffset)) newStyle.setParEffectOffset(attrs.valueAsDouble(ParagraphEffectOffset)); - static const QString ParagraphEffectIndent("ParagraphEffectIndent"); - if (attrs.hasAttribute(ParagraphEffectIndent)) - newStyle.setParEffectIndent(attrs.valueAsDouble(ParagraphEffectIndent)); - static const QString DROP("DROP"); if (attrs.hasAttribute(DROP)) newStyle.setHasDropCap(static_cast(attrs.valueAsInt(DROP))); @@ -5497,8 +5493,6 @@ PageItem* Scribus150Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut pstyle.setPeCharStyleName(attrs.valueAsString("ParagraphEffectCharStyle")); if (attrs.hasAttribute("ParagraphEffectOffset")) pstyle.setParEffectOffset(attrs.valueAsDouble("ParagraphEffectOffset")); - if (attrs.hasAttribute("ParagraphEffectIndent")) - pstyle.setParEffectIndent(attrs.valueAsDouble("ParagraphEffectIndent")); if (attrs.hasAttribute("DROP")) pstyle.setHasDropCap(static_cast(attrs.valueAsInt("DROP"))); if (attrs.hasAttribute("DROPLIN")) diff --git a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp index a6e7b4baa..ffebaef12 100644 --- a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp +++ b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp @@ -842,8 +842,6 @@ void Scribus150Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle docu.writeAttribute("ParagraphEffectCharStyle", style.peCharStyleName()); if ( ! style.isInhParEffectOffset()) docu.writeAttribute("ParagraphEffectOffset", style.parEffectOffset()); - if ( ! style.isInhParEffectIndent()) - docu.writeAttribute("ParagraphEffectIndent", static_cast(style.parEffectIndent())); if ( ! style.isInhHasDropCap()) docu.writeAttribute("DROP", static_cast(style.hasDropCap())); if ( ! style.isInhDropCapLines()) diff --git a/scribus/plugins/fileloader/scribus170format/scribus170format.cpp b/scribus/plugins/fileloader/scribus170format/scribus170format.cpp index c18650807..a08b06396 100644 --- a/scribus/plugins/fileloader/scribus170format/scribus170format.cpp +++ b/scribus/plugins/fileloader/scribus170format/scribus170format.cpp @@ -3131,10 +3131,6 @@ void Scribus170Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re if (attrs.hasAttribute(ParagraphEffectOffset)) newStyle.setParEffectOffset(attrs.valueAsDouble(ParagraphEffectOffset)); - static const QString ParagraphEffectIndent("ParagraphEffectIndent"); - if (attrs.hasAttribute(ParagraphEffectIndent)) - newStyle.setParEffectIndent(attrs.valueAsDouble(ParagraphEffectIndent)); - static const QString DROP("DROP"); if (attrs.hasAttribute(DROP)) newStyle.setHasDropCap(static_cast(attrs.valueAsInt(DROP))); @@ -5567,8 +5563,6 @@ PageItem* Scribus170Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut pstyle.setPeCharStyleName(attrs.valueAsString("ParagraphEffectCharStyle")); if (attrs.hasAttribute("ParagraphEffectOffset")) pstyle.setParEffectOffset(attrs.valueAsDouble("ParagraphEffectOffset")); - if (attrs.hasAttribute("ParagraphEffectIndent")) - pstyle.setParEffectIndent(attrs.valueAsDouble("ParagraphEffectIndent")); if (attrs.hasAttribute("DROP")) pstyle.setHasDropCap(static_cast(attrs.valueAsInt("DROP"))); if (attrs.hasAttribute("DROPLIN")) diff --git a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp index 3b7a5ec1c..1e8c88068 100644 --- a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp +++ b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp @@ -844,8 +844,6 @@ void Scribus170Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle docu.writeAttribute("ParagraphEffectCharStyle", style.peCharStyleName()); if (!style.isInhParEffectOffset()) docu.writeAttribute("ParagraphEffectOffset", style.parEffectOffset()); - if (!style.isInhParEffectIndent()) - docu.writeAttribute("ParagraphEffectIndent", static_cast(style.parEffectIndent())); if (!style.isInhHasDropCap()) docu.writeAttribute("DROP", static_cast(style.hasDropCap())); if (!style.isInhDropCapLines()) diff --git a/scribus/plugins/fileloader/scribus171format/scribus171format.cpp b/scribus/plugins/fileloader/scribus171format/scribus171format.cpp index 6c96651fb..a1372df7f 100644 --- a/scribus/plugins/fileloader/scribus171format/scribus171format.cpp +++ b/scribus/plugins/fileloader/scribus171format/scribus171format.cpp @@ -3635,10 +3635,6 @@ void Scribus171Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re if (attrs.hasAttribute(ParagraphEffectOffset)) newStyle.setParEffectOffset(attrs.valueAsDouble(ParagraphEffectOffset)); - static const QString ParagraphEffectIndent("ParagraphEffectIndent"); - if (attrs.hasAttribute(ParagraphEffectIndent)) - newStyle.setParEffectIndent(attrs.valueAsDouble(ParagraphEffectIndent)); - //Remove uppercase in 1.8 format if (attrs.hasAttribute("DROP")) newStyle.setHasDropCap(static_cast(attrs.valueAsInt("DROP"))); @@ -3695,6 +3691,10 @@ void Scribus171Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re if (attrs.hasAttribute(NumerationSuffix)) newStyle.setNumSuffix(attrs.valueAsString(NumerationSuffix)); + static const QString SuffixAlignment("SuffixAlignment"); + if (attrs.hasAttribute(SuffixAlignment)) + newStyle.setSuffixAlignment(static_cast(attrs.valueAsInt(SuffixAlignment))); + static const QString NumerationRestart("NumerationRestart"); if (attrs.hasAttribute(NumerationRestart)) newStyle.setNumRestart(attrs.valueAsInt(NumerationRestart)); @@ -6679,8 +6679,6 @@ PageItem* Scribus171Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut pstyle.setPeCharStyleName(attrs.valueAsString("ParagraphEffectCharStyle")); if (attrs.hasAttribute("ParagraphEffectOffset")) pstyle.setParEffectOffset(attrs.valueAsDouble("ParagraphEffectOffset")); - if (attrs.hasAttribute("ParagraphEffectIndent")) - pstyle.setParEffectIndent(attrs.valueAsDouble("ParagraphEffectIndent")); //Remove uppercase in 1.8 if (attrs.hasAttribute("DROP")) pstyle.setHasDropCap(static_cast(attrs.valueAsInt("DROP"))); @@ -6717,6 +6715,8 @@ PageItem* Scribus171Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut pstyle.setNumPrefix(attrs.valueAsString("NumerationPrefix")); if (attrs.hasAttribute("NumerationSuffix")) pstyle.setNumSuffix(attrs.valueAsString("NumerationSuffix")); + if (attrs.hasAttribute("SuffixAlignment")) + pstyle.setSuffixAlignment(static_cast(attrs.valueAsInt("SuffixAlignment"))); if (attrs.hasAttribute("NumerationRestart")) { NumerationRange numRange = (NumerationRange) attrs.valueAsInt("NumerationRestart"); diff --git a/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp b/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp index ae959bec6..2608bc355 100644 --- a/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp +++ b/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp @@ -846,8 +846,6 @@ void Scribus171Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle docu.writeAttribute("ParagraphEffectCharStyle", style.peCharStyleName()); if (!style.isInhParEffectOffset()) docu.writeAttribute("ParagraphEffectOffset", style.parEffectOffset()); - if (!style.isInhParEffectIndent()) - docu.writeAttribute("ParagraphEffectIndent", static_cast(style.parEffectIndent())); if (!style.isInhHasDropCap()) docu.writeAttribute("HasDropCap", static_cast(style.hasDropCap())); if (!style.isInhDropCapLines()) @@ -868,6 +866,8 @@ void Scribus171Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle docu.writeAttribute("NumerationPrefix", style.numPrefix()); if (!style.isInhNumSuffix()) docu.writeAttribute("NumerationSuffix", style.numSuffix()); + if (!style.isInhSuffixAlignment()) + docu.writeAttribute("SuffixAlignment", style.suffixAlignment()); if (!style.isInhNumStart()) docu.writeAttribute("NumerationStart", style.numStart()); if (!style.isInhNumRestart()) diff --git a/scribus/scribusdoc.cpp b/scribus/scribusdoc.cpp index c55f7634f..3faf9ae00 100644 --- a/scribus/scribusdoc.cpp +++ b/scribus/scribusdoc.cpp @@ -17731,6 +17731,11 @@ void ScribusDoc::setNumerationCounter(const QString& numName, int level, int num numS->m_counters.insert(level, number); else numS->m_counters.replace(level, number); + + while (numS->m_maxCounters.count() <= level) + numS->m_maxCounters.append(0); + if (number > numS->m_maxCounters.at(level)) + numS->m_maxCounters.replace(level, number); } int ScribusDoc::updateLocalNums(StoryText& itemText) diff --git a/scribus/styles/paragraphstyle.attrdefs.cxx b/scribus/styles/paragraphstyle.attrdefs.cxx index 238e71a5c..bbd0c0525 100644 --- a/scribus/styles/paragraphstyle.attrdefs.cxx +++ b/scribus/styles/paragraphstyle.attrdefs.cxx @@ -40,7 +40,6 @@ ATTRDEF(bool, keepTogether, KeepTogether, false) ATTRDEF(bool, hasDropCap, HasDropCap, false) ATTRDEF(int, dropCapLines, DropCapLines, 2) ATTRDEF(double, parEffectOffset, ParEffectOffset, 0.0) -ATTRDEF(bool, parEffectIndent, ParEffectIndent, false) ATTRDEF(QString, peCharStyleName, PeCharStyleName,"") ATTRDEF(bool, hasBullet, HasBullet, false) ATTRDEF(QString, bulletStr, BulletStr, QString(QChar(0x2022))) @@ -54,6 +53,7 @@ ATTRDEF(int, numStart, NumStart, 1) ATTRDEF(int, numRestart, NumRestart, 0) ATTRDEF(bool, numOther, NumOther, false) ATTRDEF(bool, numHigher, NumHigher, true) +ATTRDEF(ParagraphStyle::SuffixAlignment, suffixAlignment, SuffixAlignment, ParagraphStyle::SuffixAlign_Left) ATTRDEF(QString, backgroundColor, BackgroundColor, "None") ATTRDEF(double, backgroundShade, BackgroundShade, 100) ATTRDEF(int, hyphenConsecutiveLines, HyphenConsecutiveLines, 2) diff --git a/scribus/styles/paragraphstyle.cpp b/scribus/styles/paragraphstyle.cpp index f182104f3..9384ef24e 100644 --- a/scribus/styles/paragraphstyle.cpp +++ b/scribus/styles/paragraphstyle.cpp @@ -208,6 +208,14 @@ void ParagraphStyle::setStyle(const ParagraphStyle & other) #undef ATTRDEF } +ParagraphStyle::SuffixAlignment ParagraphStyle::flipSuffixAlignmentForRTL(SuffixAlignment align) const +{ + if (align == ParagraphStyle::SuffixAlign_Left) + return ParagraphStyle::SuffixAlign_Right; + if (align == ParagraphStyle::SuffixAlign_Right) + return ParagraphStyle::SuffixAlign_Left; + return align; +} void ParagraphStyle::getNamedResources(ResourceCollection& lists) const { @@ -261,6 +269,11 @@ static QString toXMLString(ParagraphStyle::AlignmentType val) return QString::number(static_cast(val)); } +static QString toXMLString(ParagraphStyle::SuffixAlignment val) +{ + return QString::number(static_cast(val)); +} + static QString toXMLString(ParagraphStyle::DirectionType val) { return QString::number(static_cast(val)); @@ -344,6 +357,12 @@ ParagraphStyle::AlignmentType parse(const Xml_str return parseEnum(str); } +template<> +ParagraphStyle::SuffixAlignment parse(const Xml_string& str) +{ + return parseEnum(str); +} + template<> ParagraphStyle::DirectionType parse(const Xml_string& str) { diff --git a/scribus/styles/paragraphstyle.h b/scribus/styles/paragraphstyle.h index 4e5bac2a1..2f8303791 100644 --- a/scribus/styles/paragraphstyle.h +++ b/scribus/styles/paragraphstyle.h @@ -76,6 +76,13 @@ public: CenterTab = 4 }; + enum SuffixAlignment + { + SuffixAlign_Left = 0, + SuffixAlign_Center = 1, + SuffixAlign_Right = 2 + }; + struct TabRecord { qreal tabPosition {0.0}; @@ -113,6 +120,7 @@ public: void eraseStyle(const ParagraphStyle& other); void setStyle(const ParagraphStyle& other); void erase() override { eraseStyle(*this); } + ParagraphStyle::SuffixAlignment flipSuffixAlignmentForRTL(ParagraphStyle::SuffixAlignment align) const; StyleContext* charStyleContext() { return & m_cstyleContext; } const StyleContext* charStyleContext() const { return & m_cstyleContext; } diff --git a/scribus/text/sctext_shared.cpp b/scribus/text/sctext_shared.cpp index d984c7901..d7d7a882f 100644 --- a/scribus/text/sctext_shared.cpp +++ b/scribus/text/sctext_shared.cpp @@ -68,6 +68,7 @@ void ScText_Shared::clear() if (marksCount > 0) marksCountChanged = true; marksCount = 0; + maxParEffectWidthCache.clear(); } ScText_Shared& ScText_Shared::operator= (const ScText_Shared& other) diff --git a/scribus/text/sctext_shared.h b/scribus/text/sctext_shared.h index d16bd8267..2de71bbbb 100644 --- a/scribus/text/sctext_shared.h +++ b/scribus/text/sctext_shared.h @@ -2,6 +2,7 @@ #define SCTEXT_SHARED_H #include +#include #include #include #include @@ -35,6 +36,10 @@ public: ParagraphStyle trailingStyle; CharStyle orphanedCharStyle; + /// max list-marker width per list key, shared by every frame in the chain; + /// cleared whenever text or styles change + QHash maxParEffectWidthCache; + void clear(); /** diff --git a/scribus/text/storytext.cpp b/scribus/text/storytext.cpp index 4214bb583..5c9f2b468 100644 --- a/scribus/text/storytext.cpp +++ b/scribus/text/storytext.cpp @@ -1391,6 +1391,11 @@ void StoryText::clearFlag(int pos, LayoutFlags flags) d->at(pos)->setEffects(~(flags & ScStyle_NonUserStyles) & d->at(pos)->effects().value); } +QHash &StoryText::maxParEffectWidthCache() +{ + return d->maxParEffectWidthCache; +} + const CharStyle & StoryText::charStyle() const { @@ -2261,6 +2266,10 @@ void StoryText::invalidate(int firstItem, int endItem) if (par) par->charStyleContext()->invalidate(); } + // a list marker's max width can depend on any character in the story, + // so any edit can change it; drop the whole cache to stay safe + d->maxParEffectWidthCache.clear(); + if (!signalsBlocked()) emit changed(firstItem, endItem); } diff --git a/scribus/text/storytext.h b/scribus/text/storytext.h index 7920d56ab..9aaf6effe 100644 --- a/scribus/text/storytext.h +++ b/scribus/text/storytext.h @@ -29,6 +29,7 @@ pageitem.cpp - description #include #include #include +#include #include #include "itextsource.h" @@ -290,6 +291,10 @@ public: void setFlag(int pos, LayoutFlags flag) override; void clearFlag(int pos, LayoutFlags flag) override; + /// cache of max list-marker width per list key; shared across every + /// frame referencing this story, invalidated on any edit + QHash& maxParEffectWidthCache(); + // when physical view doesn't match logical view any more: /// call this if the shape of an embedded object changes (redos layout) diff --git a/scribus/ui/propertywidget_pareffect.cpp b/scribus/ui/propertywidget_pareffect.cpp index 0e6ef1e52..1a3701ea5 100644 --- a/scribus/ui/propertywidget_pareffect.cpp +++ b/scribus/ui/propertywidget_pareffect.cpp @@ -45,6 +45,10 @@ PropertyWidget_ParEffect::PropertyWidget_ParEffect(QWidget *parent) : QFrame(par peCombo->setCurrentIndex(0); setType(peCombo->currentData().toInt()); + suffixAlignmentCombo->addItem(tr("Left"), ParagraphStyle::SuffixAlign_Left); + suffixAlignmentCombo->addItem(tr("Center"), ParagraphStyle::SuffixAlign_Center); + suffixAlignmentCombo->addItem(tr("Right"), ParagraphStyle::SuffixAlign_Right); + iconSetChange(); connect(ScQApp, SIGNAL(iconSetChanged()), this, SLOT(iconSetChange())); @@ -185,6 +189,9 @@ void PropertyWidget_ParEffect::setType(int id) stackedWidget->setVisible(true); peGroup->setVisible(true); } + bool isNumberedList = (id == 2); + suffixAlignmentLabel->setVisible(isNumberedList); + suffixAlignmentCombo->setVisible(isNumberedList); } void PropertyWidget_ParEffect::fillBulletStrEditCombo() @@ -230,8 +237,8 @@ void PropertyWidget_ParEffect::updateStyle(const ParagraphStyle& newPStyle) QSignalBlocker blocker8(numSuffix); QSignalBlocker blocker9(numStart); QSignalBlocker blockerA(peOffset); - QSignalBlocker blockerB(peIndent); QSignalBlocker blockerC(peCharStyleCombo); + QSignalBlocker blockerD(suffixAlignmentCombo); if (newPStyle.hasDropCap()) { @@ -269,9 +276,16 @@ void PropertyWidget_ParEffect::updateStyle(const ParagraphStyle& newPStyle) numFormatCombo->setCurrentFormat((NumFormat) newPStyle.numFormat()); peOffset->setValue(newPStyle.parEffectOffset() * m_unitRatio); - peIndent->setChecked(newPStyle.parEffectIndent()); showCharStyle(newPStyle.peCharStyleName()); + ParagraphStyle::SuffixAlignment displayAlign = newPStyle.suffixAlignment(); + if (newPStyle.direction() == ParagraphStyle::RTL) + displayAlign = newPStyle.flipSuffixAlignmentForRTL(displayAlign); + + int idx = suffixAlignmentCombo->findData(static_cast(displayAlign)); + if (idx >= 0) + suffixAlignmentCombo->setCurrentIndex(idx); + if (oldPeComboIndex != peCombo->currentIndex()) emit needsRelayout(); } @@ -288,7 +302,7 @@ void PropertyWidget_ParEffect::connectSignals() connect(numSuffix, SIGNAL(textChanged(QString)), this, SLOT(handleNumSuffix(QString)), Qt::UniqueConnection); connect(numStart, SIGNAL(valueChanged(int)), this, SLOT(handleNumStart(int)), Qt::UniqueConnection); connect(peOffset, SIGNAL(valueChanged(double)), this, SLOT(handlePEOffset(double)), Qt::UniqueConnection); - connect(peIndent, SIGNAL(toggled(bool)), this, SLOT(handlePEIndent(bool)), Qt::UniqueConnection); + connect(suffixAlignmentCombo, SIGNAL(activated(int)), this, SLOT(handleSuffixAlignment(int)), Qt::UniqueConnection); connect(peCharStyleCombo, SIGNAL(newStyle(QString)), this, SLOT(handlePECharStyle(QString)), Qt::UniqueConnection); } @@ -304,7 +318,7 @@ void PropertyWidget_ParEffect::disconnectSignals() disconnect(numSuffix, SIGNAL(textChanged(QString)), this, SLOT(handleNumSuffix(QString))); disconnect(numStart, SIGNAL(valueChanged(int)), this, SLOT(handleNumStart(int))); disconnect(peOffset, SIGNAL(valueChanged(double)), this, SLOT(handlePEOffset(double))); - disconnect(peIndent, SIGNAL(toggled(bool)), this, SLOT(handlePEIndent(bool))); + disconnect(suffixAlignmentCombo, SIGNAL(activated(int)), this, SLOT(handleSuffixAlignment(int))); disconnect(peCharStyleCombo, SIGNAL(newStyle(QString)), this, SLOT(handlePECharStyle(QString))); } @@ -403,8 +417,6 @@ void PropertyWidget_ParEffect::handleParEffectUse() newStyle.setHasBullet(false); newStyle.setHasNum(false); } - newStyle.setParEffectOffset(peOffset->value() / m_unitRatio); - newStyle.setParEffectIndent(peIndent->isChecked()); setType(peCombo->currentData().toInt()); @@ -537,12 +549,15 @@ void PropertyWidget_ParEffect::handlePEOffset(double offset) handleChanges(m_item, newStyle); } -void PropertyWidget_ParEffect::handlePEIndent(bool indent) +void PropertyWidget_ParEffect::handleSuffixAlignment(int index) { if (!m_doc || !m_item) return; ParagraphStyle newStyle; - newStyle.setParEffectIndent(indent); + auto align = static_cast(suffixAlignmentCombo->itemData(index).toInt()); + if (m_item->currentStyle().direction() == ParagraphStyle::RTL) + align = newStyle.flipSuffixAlignmentForRTL(align); + newStyle.setSuffixAlignment(align); handleChanges(m_item, newStyle); } diff --git a/scribus/ui/propertywidget_pareffect.h b/scribus/ui/propertywidget_pareffect.h index 62729f7c2..54d2aeab4 100644 --- a/scribus/ui/propertywidget_pareffect.h +++ b/scribus/ui/propertywidget_pareffect.h @@ -66,7 +66,7 @@ public slots: void handleNumSuffix(const QString&); void handleNumStart(int); void handlePEOffset(double); - void handlePEIndent(bool); + void handleSuffixAlignment(int); void handlePECharStyle(const QString&); private slots: diff --git a/scribus/ui/propertywidget_pareffectbase.ui b/scribus/ui/propertywidget_pareffectbase.ui index 1a2670a01..fa21acf2e 100644 --- a/scribus/ui/propertywidget_pareffectbase.ui +++ b/scribus/ui/propertywidget_pareffectbase.ui @@ -438,10 +438,17 @@ - - + + - Auto-Indent + Suffix Align: + + + + + + + Alignment of suffix characters in numbered lists @@ -556,8 +563,8 @@ numStart numPrefix numSuffix - peOffset - peIndent +peOffset + suffixAlignmentCombo peCharStyleCombo dropCapLines bulletStrEdit diff --git a/scribus/ui/smpstylewidget.cpp b/scribus/ui/smpstylewidget.cpp index 12fde4428..d5e0fc626 100644 --- a/scribus/ui/smpstylewidget.cpp +++ b/scribus/ui/smpstylewidget.cpp @@ -25,7 +25,6 @@ static bool isEqual(double a, double b) return al == bl; } - SMPStyleWidget::SMPStyleWidget(ScribusDoc* doc, StyleSet *cstyles) : m_Doc(doc), m_cstyles(cstyles) @@ -74,6 +73,7 @@ SMPStyleWidget::SMPStyleWidget(ScribusDoc* doc, StyleSet *cstyles) : dropCapLines->setMaximum(99); fillPECombo(); + fillSuffixAlignmentCombo(); connect(peCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(handleParEffectUse(int))); connect(parentParEffectsButton, SIGNAL(clicked()), this, SLOT(slotParentParEffects())); @@ -156,7 +156,7 @@ void SMPStyleWidget::languageChange() numRestartCombo->blockSignals(numRestartComboBlocked); fillPECombo(); - + fillSuffixAlignmentCombo(); backgroundColor->colorButton->setPersistentToolTip( tr("Background color of selected text")); backgroundColor->setText(tr("Background")); } @@ -303,8 +303,19 @@ void SMPStyleWidget::show(ParagraphStyle *pstyle, QList &pstyles parEffectOffset->setValue(pstyle->parEffectOffset() * unitRatio, pstyle->isInhParEffectOffset()); parEffectOffset->setParentValue(parent->parEffectOffset() * unitRatio); - parEffectIndentBox->setChecked(pstyle->parEffectIndent(),pstyle->isInhParEffectIndent()); - parEffectIndentBox->setParentValue(parent->parEffectIndent()); + + ParagraphStyle::SuffixAlignment displayAlign = pstyle->suffixAlignment(); + if (pstyle->direction() == ParagraphStyle::RTL) + displayAlign = pstyle->flipSuffixAlignmentForRTL(displayAlign); + + ParagraphStyle::SuffixAlignment parentDisplayAlign = parent->suffixAlignment(); + if (parent->direction() == ParagraphStyle::RTL) + parentDisplayAlign = pstyle->flipSuffixAlignmentForRTL(parentDisplayAlign); + + int suffixAlignIndex = suffixAlignmentCombo->findData(static_cast(displayAlign)); + suffixAlignmentCombo->setCurrentItem(suffixAlignIndex, pstyle->isInhSuffixAlignment()); + suffixAlignmentCombo->setParentItem(suffixAlignmentCombo->findData(static_cast(parentDisplayAlign))); + dropCapLines->setValue(pstyle->dropCapLines(), pstyle->isInhDropCapLines()); dropCapLines->setParentValue(parent->dropCapLines()); bulletStrEdit->setEditText(pstyle->bulletStr()); @@ -356,10 +367,13 @@ void SMPStyleWidget::show(ParagraphStyle *pstyle, QList &pstyles maxGlyphExtSpin->setValue(pstyle->maxGlyphExtension() * 100.0); maxConsecutiveCountSpinBox->setValue(pstyle->hyphenConsecutiveLines()); parEffectOffset->setValue(pstyle->parEffectOffset() * unitRatio); - parEffectIndentBox->setChecked(pstyle->parEffectIndent()); - parentParEffectsButton->hide(); - disconnect(parentParEffectsButton, SIGNAL(clicked()), this, SLOT(slotParentParEffects())); + ParagraphStyle::SuffixAlignment displayAlign = pstyle->suffixAlignment(); + if (pstyle->direction() == ParagraphStyle::RTL) + displayAlign = pstyle->flipSuffixAlignmentForRTL(displayAlign); + + int suffixAlignIndex = suffixAlignmentCombo->findData(static_cast(displayAlign)); + suffixAlignmentCombo->setCurrentItem((suffixAlignIndex >= 0) ? suffixAlignIndex : 0); dropCapLines->setValue(pstyle->dropCapLines()); bulletStrEdit->setEditText(pstyle->bulletStr()); setWidgetBoldFont(bulletCharLabel, false); @@ -1000,6 +1014,17 @@ void SMPStyleWidget::fillPECombo() peCombo->setCurrentIndex(currIndex); } +void SMPStyleWidget::fillSuffixAlignmentCombo() +{ + QSignalBlocker sb(suffixAlignmentCombo); + int currIndex = suffixAlignmentCombo->currentIndex(); + suffixAlignmentCombo->clear(); + suffixAlignmentCombo->addItem(tr("Left"), ParagraphStyle::SuffixAlign_Left); + suffixAlignmentCombo->addItem(tr("Center"), ParagraphStyle::SuffixAlign_Center); + suffixAlignmentCombo->addItem(tr("Right"), ParagraphStyle::SuffixAlign_Right); + suffixAlignmentCombo->setCurrentIndex(currIndex); +} + void SMPStyleWidget::setParagraphEffect(int index) { QSignalBlocker sigPECombo(peCombo); @@ -1030,6 +1055,9 @@ void SMPStyleWidget::setParagraphEffect(int index) peGroup->setVisible(false); peCombo->setCurrentIndex(0); } + bool isNumberedList = (id == 3); + suffixAlignmentLabel->setVisible(isNumberedList); + suffixAlignmentCombo->setVisible(isNumberedList); } void SMPStyleWidget::showDropCap(const QList &pstyles, const QList &cstyles, int unitIndex) diff --git a/scribus/ui/smpstylewidget.h b/scribus/ui/smpstylewidget.h index c985d7033..4cb5c88bd 100644 --- a/scribus/ui/smpstylewidget.h +++ b/scribus/ui/smpstylewidget.h @@ -52,6 +52,7 @@ private: void fillNumerationsCombo(); void fillNumRestartCombo(); void fillPECombo(); + void fillSuffixAlignmentCombo(); void setParagraphEffect(int); void showColors(const QList &cstyles); void showLineSpacing(const QList &pstyles); diff --git a/scribus/ui/smpstylewidget.ui b/scribus/ui/smpstylewidget.ui index 8a6d90e31..f62551956 100644 --- a/scribus/ui/smpstylewidget.ui +++ b/scribus/ui/smpstylewidget.ui @@ -29,7 +29,7 @@ - 0 + 1 @@ -181,7 +181,7 @@ - + 0 @@ -280,7 +280,7 @@ A value of 0 means unlimited hyphenations. 4 - + 0 @@ -326,7 +326,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -379,7 +379,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -389,7 +389,7 @@ A value of 0 means unlimited hyphenations. - + true @@ -419,7 +419,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -459,7 +459,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -469,7 +469,7 @@ A value of 0 means unlimited hyphenations. - + true @@ -597,7 +597,7 @@ A value of 0 means unlimited hyphenations. 16 - + 0 @@ -673,7 +673,7 @@ A value of 0 means unlimited hyphenations. 16 - + 0 @@ -710,7 +710,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -804,7 +804,7 @@ A value of 0 means unlimited hyphenations. 16 - + 0 @@ -844,7 +844,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -1021,7 +1021,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -1441,7 +1441,7 @@ A value of 0 means unlimited hyphenations. - + 0 @@ -1451,10 +1451,7 @@ A value of 0 means unlimited hyphenations. New Set: - - FormWidget::LabelPosition::Left - - + false @@ -1476,6 +1473,20 @@ A value of 0 means unlimited hyphenations. + + + + Alignment of suffix characters in numbered lists + + + + + + + Suffix Align: + + + @@ -1506,7 +1517,7 @@ A value of 0 means unlimited hyphenations. 16 - + 0 @@ -1545,46 +1556,6 @@ A value of 0 means unlimited hyphenations. - - - - - 0 - 0 - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Hang Paragraph Effect before paragraph indent - - - Auto-Indent - - - - - - @@ -1606,7 +1577,7 @@ A value of 0 means unlimited hyphenations. 16 - + 0 @@ -1792,7 +1763,6 @@ A value of 0 means unlimited hyphenations. parentParEffectsButton dropCapLines parEffectOffset - parEffectIndentBox parEffectCharStyleCombo numPrefix numSuffix diff --git a/scribus/ui/smtextstyles.cpp b/scribus/ui/smtextstyles.cpp index b021322dd..390365f53 100644 --- a/scribus/ui/smtextstyles.cpp +++ b/scribus/ui/smtextstyles.cpp @@ -37,7 +37,6 @@ for which a new license (GPL+exception) is in place. #include "units.h" #include "util.h" - SMParagraphStyle::SMParagraphStyle(SMCharacterStyle* cstyleItem): m_cstyleItem(cstyleItem) { @@ -511,7 +510,7 @@ void SMParagraphStyle::setupConnections() connect(m_pwidget->peCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPargraphEffects(int))); connect(m_pwidget->dropCapLines, SIGNAL(valueChanged(int)), this, SLOT(slotDropCapLines(int))); connect(m_pwidget->parEffectOffset, SIGNAL(valueChanged(double)), this, SLOT(slotParEffectOffset())); - connect(m_pwidget->parEffectIndentBox, SIGNAL(toggled(bool)), this, SLOT(slotParEffectIndent(bool))); + connect(m_pwidget->suffixAlignmentCombo, SIGNAL(activated(int)), this, SLOT(slotSuffixAlignment(int))); connect(m_pwidget->parEffectCharStyleCombo, SIGNAL(activated(int)), this, SLOT(slotParEffectCharStyle(int))); connect(m_pwidget->bulletStrEdit, SIGNAL(editTextChanged(QString)), this, SLOT(slotBulletStr(QString))); connect(m_pwidget->numComboBox, SIGNAL(textActivated(QString)), this, SLOT(slotNumName(QString))); @@ -606,8 +605,7 @@ void SMParagraphStyle::removeConnections() disconnect(m_pwidget->peCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotPargraphEffects(int))); disconnect(m_pwidget->dropCapLines, SIGNAL(valueChanged(int)), this, SLOT(slotDropCapLines(int))); disconnect(m_pwidget->parEffectOffset, SIGNAL(valueChanged(double)), this, SLOT(slotParEffectOffset())); - disconnect(m_pwidget->parEffectIndentBox, SIGNAL(toggled(bool)), this, SLOT(slotParEffectIndent(bool))); - disconnect(m_pwidget->parEffectCharStyleCombo, SIGNAL(activated(int)), this, SLOT(slotParEffectCharStyle(int))); + disconnect(m_pwidget->suffixAlignmentCombo, SIGNAL(activated(int)), this, SLOT(slotSuffixAlignment(int))); disconnect(m_pwidget->bulletStrEdit, SIGNAL(editTextChanged(QString)), this, SLOT(slotBulletStr(QString))); disconnect(m_pwidget->numComboBox, SIGNAL(textActivated(QString)), this, SLOT(slotNumName(QString))); disconnect(m_pwidget->numFormatCombo, SIGNAL(activated(int)), this, SLOT(slotNumFormat(int))); @@ -953,17 +951,25 @@ void SMParagraphStyle::slotParEffectOffset() slotSelectionDirty(); } -void SMParagraphStyle::slotParEffectIndent(bool isOn) +void SMParagraphStyle::slotSuffixAlignment(int index) { - if (m_pwidget->parEffectIndentBox->useParentValue()) + if (m_pwidget->suffixAlignmentCombo->useParentValue()) + { for (int i = 0; i < m_selection.count(); ++i) - m_selection[i]->resetParEffectIndent(); - else + m_selection[i]->resetSuffixAlignment(); + } + else { + auto align = static_cast(m_pwidget->suffixAlignmentCombo->itemData(index).toInt()); for (int i = 0; i < m_selection.count(); ++i) - m_selection[i]->setParEffectIndent(isOn); + { + ParagraphStyle::SuffixAlignment styleAlign = align; + if (m_selection[i]->direction() == ParagraphStyle::RTL) + styleAlign = m_selection[i]->flipSuffixAlignmentForRTL(styleAlign); + m_selection[i]->setSuffixAlignment(styleAlign); + } } - + slotSelectionDirty(); } diff --git a/scribus/ui/smtextstyles.h b/scribus/ui/smtextstyles.h index 3dd02356a..087870f22 100644 --- a/scribus/ui/smtextstyles.h +++ b/scribus/ui/smtextstyles.h @@ -77,7 +77,7 @@ private slots: void slotPargraphEffects(int index); void slotDropCapLines(int lines); void slotParEffectOffset(); - void slotParEffectIndent(bool); + void slotSuffixAlignment(int); void slotParEffectCharStyle(int); void slotBulletStr(const QString &str); void slotNumName(const QString &str);