View Issue Details

IDProjectCategoryView StatusLast Update
0017768ScribusStylespublic2026-07-11 06:27
Reporterqirat Assigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status newResolutionopen 
PlatformLinuxOSFedora WorkstationOS Version43
Summary0017768: "Align Suffix" option for Numbered Lists
DescriptionI request the feature to have an "align suffix" option for Numbered Lists. Not only would it help the regular lists but TOCs as well.

Currently, TOCs as numbered lists align to the very left as normal text, which makes it look off because single numbers (1-9) and double (10 and up) do not align in the list.

Having the option to align the suffix would be a huge improvement to make TOCs visually appealing. Kindly, check the attached image.
Additional InformationThe attached image:
1. When creating a paragraph-style-based TOC, H1 does not inherit its paragraph style when it appears in the "TOC" text-frame as the generated TOC is a brand new text.
2. So, here are paragraph styles to format the generated TOC.
3. TOC_H1 paragraph style handles H1 when it is listed in TOC, and TOC_H2 handles H2.

4. A screenshot in the image, the one with the red border is an example of what a suffix-aligned TOC would look like.
TagsNo tags attached.
Attached Files
scribus_align_suffix.jpg (455,024 bytes)   
scribus_align_suffix.jpg (455,024 bytes)   
PatchNo

Activities

qirat

2026-07-01 01:51

reporter   ~0053912

This is how ID handles it: https://www.youtube.com/watch?v=wGtMO65_8Jo

Still a bit work (and obsecure, as the gentleman said) imho. Could be simpler.

qirat

2026-07-09 04:45

reporter   ~0053998

Created with AI, this I think is a much simpler approach than InDesign (in the version used at the link above at least), and maybe other DTPs too.)
list-marker-alignment-v1.1-stylemanager-popup-fix.patch (39,524 bytes)   
diff --git a/scribus/pageitem.cpp b/scribus/pageitem.cpp
index af8d4fd..9581bb2 100644
--- a/scribus/pageitem.cpp
+++ b/scribus/pageitem.cpp
@@ -6998,7 +6998,19 @@ void PageItem::restoreParagraphStyle(SimpleState *ss, bool isUndo)
 		return;
 	}
 
-	int pos = is->getInt("POS");
+	// Paragraph-style undo states used to be anchored only by raw text
+	// positions. Numbered/bulleted-list marker characters can be inserted or
+	// removed while applying paragraph styles, so those positions may shift
+	// before undo/redo is replayed. Prefer the stored paragraph index and
+	// resolve it against the current story; keep POS as a fallback for older
+	// undo states.
+	int pos = qBound(0, is->getInt("POS"), itemText.length());
+	if (is->contains("PARA"))
+	{
+		const int maxPara = static_cast<int>(itemText.nrOfParagraphs());
+		const int para = qBound(0, is->getInt("PARA"), maxPara);
+		pos = itemText.endOfParagraph(static_cast<uint>(para));
+	}
 	if (isUndo)
 	{
 		itemText.eraseStyle(pos, is->getNewState());
diff --git a/scribus/pageitem_textframe.cpp b/scribus/pageitem_textframe.cpp
index 1c5b95c..5664b10 100644
--- a/scribus/pageitem_textframe.cpp
+++ b/scribus/pageitem_textframe.cpp
@@ -24,8 +24,10 @@ for which a new license (GPL+exception) is in place.
 #include "pageitem_textframe.h"
 
 #include <QDebug>
+#include <QHash>
 #include <QList>
 #include <QTransform>
+#include <QVector>
 #include <QPalette>
 #include <QPoint>
 #include <QPolygon>
@@ -76,6 +78,65 @@ for which a new license (GPL+exception) is in place.
 
 using namespace std;
 
+namespace
+{
+	struct NumberedListMarkerMetrics
+	{
+		double width { 0.0 };
+		double suffixAnchor { 0.0 };
+		double boxWidth { 0.0 };
+		double alignShift { 0.0 };
+	};
+
+	QString numberedListMarkerAlignmentKey(const ParagraphStyle& style)
+	{
+		return QString("%1\x1f%2\x1f%3\x1f%4\x1f%5")
+			.arg(style.numName())
+			.arg(style.numLevel())
+			.arg(style.numFormat())
+			.arg(style.numPrefix())
+			.arg(style.numSuffix());
+	}
+
+	double numberedListMarkerClusterWidth(const GlyphCluster& cluster)
+	{
+		double width = 0.0;
+		for (const GlyphLayout& glyph : cluster.glyphs())
+			width += glyph.xadvance * cluster.scaleH();
+		return width;
+	}
+
+	NumberedListMarkerMetrics numberedListMarkerMetrics(const QList<GlyphCluster>& glyphClusters, int markerStart, int markerEnd, int suffixLength)
+	{
+		NumberedListMarkerMetrics metrics;
+		QVector<double> glyphOrigins;
+		int glyphCount = 0;
+
+		for (int i = markerStart; i < markerEnd; ++i)
+			glyphCount += glyphClusters[i].glyphs().count();
+		glyphOrigins.reserve(glyphCount);
+
+		for (int i = markerStart; i < markerEnd; ++i)
+		{
+			const GlyphCluster& cluster = glyphClusters[i];
+			for (const GlyphLayout& glyph : cluster.glyphs())
+			{
+				glyphOrigins.append(metrics.width + (glyph.xoffset * cluster.scaleH()));
+				metrics.width += glyph.xadvance * cluster.scaleH();
+			}
+		}
+
+		metrics.suffixAnchor = metrics.width;
+		if (suffixLength > 0 && !glyphOrigins.isEmpty())
+		{
+			const int suffixGlyphIndex = qMax(0, glyphOrigins.count() - suffixLength);
+			metrics.suffixAnchor = glyphOrigins.at(suffixGlyphIndex);
+		}
+
+		return metrics;
+	}
+}
+
 PageItem_TextFrame::PageItem_TextFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline)
 	: PageItem(pa, PageItem::TextFrame, x, y, w, h, w2, fill, outline)
 {
@@ -1330,6 +1391,77 @@ void PageItem_TextFrame::layout()
 		ShapedTextFeed shapedText(&itemText, firstInFrame(), context);
 
 		QList<GlyphCluster> glyphClusters; // = textShaper.shape();
+		QHash<int, NumberedListMarkerMetrics> numberedListMarkerMetricsByPos;
+		bool hasAlignedNumberedListMarkers = false;
+		for (int pos = 0; pos < itLen; ++pos)
+		{
+			if (!itemText.isBlockStart(pos))
+				continue;
+			const ParagraphStyle& blockStyle = itemText.paragraphStyle(pos);
+			if (blockStyle.hasNum() && blockStyle.numAlignment() != ParagraphStyle::NumLeftAligned)
+			{
+				hasAlignedNumberedListMarkers = true;
+				break;
+			}
+		}
+		if (hasAlignedNumberedListMarkers)
+		{
+			for (int clusterIndex = 0; shapedText.haveMoreText(clusterIndex, glyphClusters); ++clusterIndex)
+			{
+			}
+
+			QHash<QString, double> maxMarkerWidthForList;
+			QHash<QString, double> maxSuffixAnchorForList;
+			QHash<int, QString> markerListKey;
+			for (int clusterIndex = 0; clusterIndex < glyphClusters.count();)
+			{
+				const int markerPos = glyphClusters[clusterIndex].firstChar();
+				const ParagraphStyle& blockStyle = itemText.paragraphStyle(markerPos);
+				if (!itemText.isBlockStart(markerPos) || !blockStyle.hasNum() || blockStyle.numAlignment() == ParagraphStyle::NumLeftAligned)
+				{
+					++clusterIndex;
+					continue;
+				}
+
+				int markerEnd = clusterIndex;
+				while (markerEnd < glyphClusters.count() && glyphClusters[markerEnd].firstChar() == markerPos)
+					++markerEnd;
+
+				NumberedListMarkerMetrics metrics = numberedListMarkerMetrics(glyphClusters, clusterIndex, markerEnd, blockStyle.numSuffix().length());
+				const QString listKey = numberedListMarkerAlignmentKey(blockStyle);
+				markerListKey.insert(markerPos, listKey);
+				numberedListMarkerMetricsByPos.insert(markerPos, metrics);
+				maxMarkerWidthForList[listKey] = qMax(maxMarkerWidthForList.value(listKey, 0.0), metrics.width);
+				maxSuffixAnchorForList[listKey] = qMax(maxSuffixAnchorForList.value(listKey, 0.0), metrics.suffixAnchor);
+				clusterIndex = markerEnd;
+			}
+
+			QHash<QString, double> markerBoxWidthForList;
+			for (auto it = markerListKey.cbegin(); it != markerListKey.cend(); ++it)
+			{
+				const int markerPos = it.key();
+				const QString& listKey = it.value();
+				const ParagraphStyle& blockStyle = itemText.paragraphStyle(markerPos);
+				NumberedListMarkerMetrics metrics = numberedListMarkerMetricsByPos.value(markerPos);
+				if (blockStyle.numAlignment() == ParagraphStyle::NumSuffixAligned)
+					metrics.alignShift = qMax(0.0, maxSuffixAnchorForList.value(listKey, metrics.suffixAnchor) - metrics.suffixAnchor);
+				markerBoxWidthForList[listKey] = qMax(markerBoxWidthForList.value(listKey, 0.0), metrics.width + metrics.alignShift);
+				numberedListMarkerMetricsByPos.insert(markerPos, metrics);
+			}
+
+			for (auto it = markerListKey.cbegin(); it != markerListKey.cend(); ++it)
+			{
+				const int markerPos = it.key();
+				const QString& listKey = it.value();
+				const ParagraphStyle& blockStyle = itemText.paragraphStyle(markerPos);
+				NumberedListMarkerMetrics metrics = numberedListMarkerMetricsByPos.value(markerPos);
+				if (blockStyle.numAlignment() == ParagraphStyle::NumSuffixAligned)
+					metrics.boxWidth = markerBoxWidthForList.value(listKey, metrics.width + metrics.alignShift);
+				else
+					metrics.boxWidth = maxMarkerWidthForList.value(listKey, metrics.width);
+				numberedListMarkerMetricsByPos.insert(markerPos, metrics);
+			}
+		}
 		// std::sort(glyphClusters.begin(), glyphClusters.end(), logicalGlyphRunComp);
 
 		LineControl current(m_width, m_height, m_textDistanceMargins, lineCorr, m_Doc, context, columnWidth(), m_columnGap);
@@ -1826,6 +1958,8 @@ void PageItem_TextFrame::layout()
 									break;
 								effectWidth += glyph.width();
 							}
+							if (BulNumMode && style.hasNum() && numberedListMarkerMetricsByPos.contains(a))
+								effectWidth = numberedListMarkerMetricsByPos.value(a).boxWidth;
 
 							if (style.direction() == ParagraphStyle::RTL)
 							{
@@ -2440,7 +2574,34 @@ void PageItem_TextFrame::layout()
 			}
 			if ((DropCmode || BulNumMode) && !outs)
 			{
-				current.xPos += style.parEffectOffset();
+				double parEffectExtraWidth = style.parEffectOffset();
+				double markerAlignShift = 0.0;
+				if (BulNumMode && style.hasNum() && numberedListMarkerMetricsByPos.contains(a))
+				{
+					const bool lastMarkerCluster = (i + 1 >= glyphClusters.count()) || (glyphClusters[i + 1].firstChar() != a);
+					if (lastMarkerCluster)
+					{
+						double markerWidth = 0.0;
+						for (int j = 0; j < current.glyphs.size(); ++j)
+						{
+							const GlyphCluster& currentGlyph = current.glyphs[j];
+							if (currentGlyph.firstChar() == a)
+								markerWidth += numberedListMarkerClusterWidth(currentGlyph);
+						}
+
+						const NumberedListMarkerMetrics markerMetrics = numberedListMarkerMetricsByPos.value(a);
+						const double markerBoxWidth = markerMetrics.boxWidth;
+						const double markerAlignPad = qMax(0.0, markerBoxWidth - markerWidth);
+						parEffectExtraWidth += markerAlignPad;
+						if (style.numAlignment() == ParagraphStyle::NumRightAligned)
+							markerAlignShift = markerAlignPad;
+						else if (style.numAlignment() == ParagraphStyle::NumCentered)
+							markerAlignShift = markerAlignPad / 2.0;
+						else if (style.numAlignment() == ParagraphStyle::NumSuffixAligned)
+							markerAlignShift = markerMetrics.alignShift;
+					}
+				}
+				current.xPos += parEffectExtraWidth;
 				// for bulleted lists, make sure offset is applied only after last bullet char
 				// for numbered lists, make sure that offset is applied only after the suffix
 				// loop over previous current.glyphs and set their extraWidth to 0.0
@@ -2454,14 +2615,23 @@ void PageItem_TextFrame::layout()
 					}
 				}
 				// set the offset for Drop Cap, Bullet & Number List
-				current.glyphs[currentIndex].extraWidth += style.parEffectOffset();
+				current.glyphs[currentIndex].extraWidth += parEffectExtraWidth;
+				if (markerAlignShift != 0.0)
+				{
+					for (int j = 0; j < current.glyphs.size(); ++j)
+					{
+						GlyphCluster& currentGlyph = current.glyphs[j];
+						if (currentGlyph.firstChar() == a)
+							currentGlyph.xoffset += markerAlignShift;
+					}
+				}
 				// RTL: the line is reversed at render time, so trailing extraWidth lands on
 				// the marker's OUTER (right) edge, not between the marker and the text. Shift
 				// the marker glyph to the right of its advance box so the reserved offset
 				// falls on the inner (left) side, next to the text. Drop caps and bullet /
 				// numbered-list markers share this offset mechanism.
 				if (style.direction() == ParagraphStyle::RTL && (DropCmode || BulNumMode))
-					current.glyphs[currentIndex].xoffset += style.parEffectOffset();
+					current.glyphs[currentIndex].xoffset += parEffectExtraWidth;
 
 				if (DropCmode)
 				{
diff --git a/scribus/plugins/fileloader/scribus150format/scribus150format.cpp b/scribus/plugins/fileloader/scribus150format/scribus150format.cpp
index 424ddcf..e713458 100644
--- a/scribus/plugins/fileloader/scribus150format/scribus150format.cpp
+++ b/scribus/plugins/fileloader/scribus150format/scribus150format.cpp
@@ -3163,6 +3163,10 @@ void Scribus150Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re
 	if (attrs.hasAttribute(NumerationFormat))
 		newStyle.setNumFormat(attrs.valueAsInt(NumerationFormat));
 
+	static const QString NumerationAlignment("NumerationAlignment");
+	if (attrs.hasAttribute(NumerationAlignment))
+		newStyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(attrs.valueAsInt(NumerationAlignment)));
+
 	static const QString NumerationLevel("NumerationLevel");
 	if (attrs.hasAttribute(NumerationLevel))
 		newStyle.setNumLevel(attrs.valueAsInt(NumerationLevel));
@@ -5515,6 +5519,8 @@ PageItem* Scribus150Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut
 		pstyle.setNumName(attrs.valueAsString("NumerationName"));
 	if (attrs.hasAttribute("NumerationFormat"))
 		pstyle.setNumFormat(attrs.valueAsInt("NumerationFormat"));
+	if (attrs.hasAttribute("NumerationAlignment"))
+		pstyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(attrs.valueAsInt("NumerationAlignment")));
 	if (attrs.hasAttribute("NumerationLevel"))
 		pstyle.setNumLevel(attrs.valueAsInt("NumerationLevel"));
 	if (attrs.hasAttribute("NumerationStart"))
diff --git a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
index a6e7b4b..155a11a 100644
--- a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
+++ b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
@@ -856,6 +856,8 @@ void Scribus150Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle
 		docu.writeAttribute("Numeration", static_cast<int>(style.hasNum()));
 	if ( ! style.isInhNumFormat())
 		docu.writeAttribute("NumerationFormat", style.numFormat());
+	if ( ! style.isInhNumAlignment())
+		docu.writeAttribute("NumerationAlignment", static_cast<int>(style.numAlignment()));
 	if ( ! style.isInhNumName())
 		docu.writeAttribute("NumerationName", style.numName());
 	if ( ! style.isInhNumLevel())
diff --git a/scribus/plugins/fileloader/scribus170format/scribus170format.cpp b/scribus/plugins/fileloader/scribus170format/scribus170format.cpp
index c186508..4397cbd 100644
--- a/scribus/plugins/fileloader/scribus170format/scribus170format.cpp
+++ b/scribus/plugins/fileloader/scribus170format/scribus170format.cpp
@@ -3171,6 +3171,10 @@ void Scribus170Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re
 	if (attrs.hasAttribute(NumerationFormat))
 		newStyle.setNumFormat(attrs.valueAsInt(NumerationFormat));
 
+	static const QString NumerationAlignment("NumerationAlignment");
+	if (attrs.hasAttribute(NumerationAlignment))
+		newStyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(attrs.valueAsInt(NumerationAlignment)));
+
 	static const QString NumerationLevel("NumerationLevel");
 	if (attrs.hasAttribute(NumerationLevel))
 		newStyle.setNumLevel(attrs.valueAsInt(NumerationLevel));
@@ -5585,6 +5589,8 @@ PageItem* Scribus170Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut
 		pstyle.setNumName(attrs.valueAsString("NumerationName"));
 	if (attrs.hasAttribute("NumerationFormat"))
 		pstyle.setNumFormat(attrs.valueAsInt("NumerationFormat"));
+	if (attrs.hasAttribute("NumerationAlignment"))
+		pstyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(attrs.valueAsInt("NumerationAlignment")));
 	if (attrs.hasAttribute("NumerationLevel"))
 		pstyle.setNumLevel(attrs.valueAsInt("NumerationLevel"));
 	if (attrs.hasAttribute("NumerationStart"))
diff --git a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
index 3b7a5ec..0861c5b 100644
--- a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
+++ b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
@@ -858,6 +858,8 @@ void Scribus170Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle
 		docu.writeAttribute("Numeration", static_cast<int>(style.hasNum()));
 	if (!style.isInhNumFormat())
 		docu.writeAttribute("NumerationFormat", style.numFormat());
+	if (!style.isInhNumAlignment())
+		docu.writeAttribute("NumerationAlignment", static_cast<int>(style.numAlignment()));
 	if (!style.isInhNumName())
 		docu.writeAttribute("NumerationName", style.numName());
 	if (!style.isInhNumLevel())
diff --git a/scribus/plugins/fileloader/scribus171format/scribus171format.cpp b/scribus/plugins/fileloader/scribus171format/scribus171format.cpp
index 6c96651..5f1a25d 100644
--- a/scribus/plugins/fileloader/scribus171format/scribus171format.cpp
+++ b/scribus/plugins/fileloader/scribus171format/scribus171format.cpp
@@ -3679,6 +3679,10 @@ void Scribus171Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re
 	if (attrs.hasAttribute(NumerationFormat))
 		newStyle.setNumFormat(attrs.valueAsInt(NumerationFormat));
 
+	static const QString NumerationAlignment("NumerationAlignment");
+	if (attrs.hasAttribute(NumerationAlignment))
+		newStyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(attrs.valueAsInt(NumerationAlignment)));
+
 	static const QString NumerationLevel("NumerationLevel");
 	if (attrs.hasAttribute(NumerationLevel))
 		newStyle.setNumLevel(attrs.valueAsInt(NumerationLevel));
@@ -6709,6 +6713,8 @@ PageItem* Scribus171Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut
 		pstyle.setNumName(attrs.valueAsString("NumerationName"));
 	if (attrs.hasAttribute("NumerationFormat"))
 		pstyle.setNumFormat(attrs.valueAsInt("NumerationFormat"));
+	if (attrs.hasAttribute("NumerationAlignment"))
+		pstyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(attrs.valueAsInt("NumerationAlignment")));
 	if (attrs.hasAttribute("NumerationLevel"))
 		pstyle.setNumLevel(attrs.valueAsInt("NumerationLevel"));
 	if (attrs.hasAttribute("NumerationStart"))
diff --git a/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp b/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
index ae959be..df8efa5 100644
--- a/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
+++ b/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
@@ -860,6 +860,8 @@ void Scribus171Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle
 		docu.writeAttribute("Numeration", static_cast<int>(style.hasNum()));
 	if (!style.isInhNumFormat())
 		docu.writeAttribute("NumerationFormat", style.numFormat());
+	if (!style.isInhNumAlignment())
+		docu.writeAttribute("NumerationAlignment", static_cast<int>(style.numAlignment()));
 	if (!style.isInhNumName())
 		docu.writeAttribute("NumerationName", style.numName());
 	if (!style.isInhNumLevel())
diff --git a/scribus/scribusdoc.cpp b/scribus/scribusdoc.cpp
index c55f763..4a52679 100644
--- a/scribus/scribusdoc.cpp
+++ b/scribus/scribusdoc.cpp
@@ -9771,6 +9771,7 @@ void ScribusDoc::itemSelection_EraseParagraphStyle(Selection* customSelection)
 						auto *is = new ScOldNewState<ParagraphStyle>(Um::SetStyle);
 						is->set("SET_PARASTYLE");
 						is->set("POS", pos);
+						is->set("PARA", static_cast<int>(currItem->itemText.nrOfParagraph(pos)));
 						is->setStates(currItem->itemText.paragraphStyle(pos), newStyle);
 						m_undoManager->action(currItem, is);
 					}
@@ -9784,6 +9785,7 @@ void ScribusDoc::itemSelection_EraseParagraphStyle(Selection* customSelection)
 				auto *is = new ScOldNewState<ParagraphStyle>(Um::SetStyle);
 				is->set("SET_PARASTYLE");
 				is->set("POS", stop);
+				is->set("PARA", static_cast<int>(currItem->itemText.nrOfParagraph(stop)));
 				is->setStates(currItem->itemText.paragraphStyle(stop), newStyle2);
 				m_undoManager->action(currItem, is);
 			}
@@ -9956,6 +9958,7 @@ void ScribusDoc::itemSelection_ApplyParagraphStyle(const ParagraphStyle & newSty
 						auto *is = new ScOldNewState<ParagraphStyle>(Um::SetStyle);
 						is->set("APPLY_PARASTYLE");
 						is->set("POS", pos);
+						is->set("PARA", static_cast<int>(currItem->itemText.nrOfParagraph(pos)));
 						is->setStates(currItem->itemText.paragraphStyle(pos), newStyle);
 						m_undoManager->action(currItem, is);
 					}
@@ -9967,6 +9970,7 @@ void ScribusDoc::itemSelection_ApplyParagraphStyle(const ParagraphStyle & newSty
 				auto *is = new ScOldNewState<ParagraphStyle>(Um::SetStyle);
 				is->set("APPLY_PARASTYLE");
 				is->set("POS", stop);
+				is->set("PARA", static_cast<int>(currItem->itemText.nrOfParagraph(stop)));
 				is->setStates(currItem->itemText.paragraphStyle(stop), newStyle);
 				m_undoManager->action(currItem, is);
 			}
diff --git a/scribus/styles/paragraphstyle.attrdefs.cxx b/scribus/styles/paragraphstyle.attrdefs.cxx
index 238e71a..271f43e 100644
--- a/scribus/styles/paragraphstyle.attrdefs.cxx
+++ b/scribus/styles/paragraphstyle.attrdefs.cxx
@@ -47,6 +47,7 @@ ATTRDEF(QString, bulletStr, BulletStr, QString(QChar(0x2022)))
 ATTRDEF(bool, hasNum, HasNum, false)
 ATTRDEF(QString, numName, NumName, "")
 ATTRDEF(int, numFormat, NumFormat, 0)
+ATTRDEF(ParagraphStyle::NumAlignment, numAlignment, NumAlignment, ParagraphStyle::NumLeftAligned)
 ATTRDEF(QString, numPrefix, NumPrefix, "")
 ATTRDEF(QString, numSuffix, NumSuffix, ".")
 ATTRDEF(int, numLevel, NumLevel, 0)
diff --git a/scribus/styles/paragraphstyle.cpp b/scribus/styles/paragraphstyle.cpp
index f182104..97f0683 100644
--- a/scribus/styles/paragraphstyle.cpp
+++ b/scribus/styles/paragraphstyle.cpp
@@ -266,6 +266,11 @@ static QString toXMLString(ParagraphStyle::DirectionType val)
 	return QString::number(static_cast<int>(val));
 }
 
+static QString toXMLString(ParagraphStyle::NumAlignment val)
+{
+	return QString::number(static_cast<int>(val));
+}
+
 static QString toXMLString(const QList<ParagraphStyle::TabRecord> & )
 {
 	return "dummy";
@@ -356,6 +361,12 @@ ParagraphStyle::LineSpacingMode parse<ParagraphStyle::LineSpacingMode>(const Xml
 	return parseEnum<ParagraphStyle::LineSpacingMode>(str);
 }
 
+template<>
+ParagraphStyle::NumAlignment parse<ParagraphStyle::NumAlignment>(const Xml_string& str)
+{
+	return parseEnum<ParagraphStyle::NumAlignment>(str);
+}
+
 
 using Tablist = QList<ParagraphStyle::TabRecord>;
 
diff --git a/scribus/styles/paragraphstyle.h b/scribus/styles/paragraphstyle.h
index 4e5bac2..562823a 100644
--- a/scribus/styles/paragraphstyle.h
+++ b/scribus/styles/paragraphstyle.h
@@ -50,6 +50,14 @@ public:
 		RTL  = 1
 	};
 
+	enum NumAlignment
+	{
+		NumLeftAligned    = 0,
+		NumCentered       = 1,
+		NumRightAligned   = 2,
+		NumSuffixAligned  = 3
+	};
+
 	enum OpticalMarginType
 	{
 		OM_None  = 0,
diff --git a/scribus/ui/propertywidget_pareffect.cpp b/scribus/ui/propertywidget_pareffect.cpp
index 0e6ef1e..d57194e 100644
--- a/scribus/ui/propertywidget_pareffect.cpp
+++ b/scribus/ui/propertywidget_pareffect.cpp
@@ -19,6 +19,7 @@ for which a new license (GPL+exception) is in place.
 #include "scribusapp.h"
 #include "scribusdoc.h"
 #include "selection.h"
+#include "styles/paragraphstyle.h"
 #include "util.h"
 
 PropertyWidget_ParEffect::PropertyWidget_ParEffect(QWidget *parent) : QFrame(parent)
@@ -31,6 +32,7 @@ PropertyWidget_ParEffect::PropertyWidget_ParEffect(QWidget *parent) : QFrame(par
 	if (m_doc)
 		peCharStyleCombo->updateStyleList();
 	fillBulletStrEditCombo();
+	fillNumAlignmentCombo();
 
 	numStart->setMinimum(1);
 	numStart->setMaximum(9999);
@@ -198,6 +200,21 @@ void PropertyWidget_ParEffect::fillBulletStrEditCombo()
 		bulletStrEdit->setEditText(QChar(0x2022));
 }
 
+void PropertyWidget_ParEffect::fillNumAlignmentCombo()
+{
+	QSignalBlocker sb(numAlignmentCombo);
+	const int currentData = numAlignmentCombo->currentData().toInt();
+	numAlignmentCombo->clear();
+	numAlignmentCombo->addItem(tr("Left"), static_cast<int>(ParagraphStyle::NumLeftAligned));
+	numAlignmentCombo->addItem(tr("Center"), static_cast<int>(ParagraphStyle::NumCentered));
+	numAlignmentCombo->addItem(tr("Right"), static_cast<int>(ParagraphStyle::NumRightAligned));
+	numAlignmentCombo->addItem(tr("Suffix"), static_cast<int>(ParagraphStyle::NumSuffixAligned));
+	int index = numAlignmentCombo->findData(currentData);
+	if (index < 0)
+		index = 0;
+	numAlignmentCombo->setCurrentIndex(index);
+}
+
 void PropertyWidget_ParEffect::fillPECombo()
 {
 	QSignalBlocker sb(peCombo);
@@ -229,6 +246,7 @@ void PropertyWidget_ParEffect::updateStyle(const ParagraphStyle& newPStyle)
 	QSignalBlocker blocker7(numPrefix);
 	QSignalBlocker blocker8(numSuffix);
 	QSignalBlocker blocker9(numStart);
+	QSignalBlocker blockerD(numAlignmentCombo);
 	QSignalBlocker blockerA(peOffset);
 	QSignalBlocker blockerB(peIndent);
 	QSignalBlocker blockerC(peCharStyleCombo);
@@ -266,6 +284,10 @@ void PropertyWidget_ParEffect::updateStyle(const ParagraphStyle& newPStyle)
 	numPrefix->setText(newPStyle.numPrefix());
 	numSuffix->setText(newPStyle.numSuffix());
 	numStart->setValue(newPStyle.numStart());
+	int numAlignmentIndex = numAlignmentCombo->findData(static_cast<int>(newPStyle.numAlignment()));
+	if (numAlignmentIndex < 0)
+		numAlignmentIndex = 0;
+	numAlignmentCombo->setCurrentIndex(numAlignmentIndex);
 
 	numFormatCombo->setCurrentFormat((NumFormat) newPStyle.numFormat());
 	peOffset->setValue(newPStyle.parEffectOffset() * m_unitRatio);
@@ -284,6 +306,7 @@ void PropertyWidget_ParEffect::connectSignals()
 	connect(numComboBox, SIGNAL(textActivated(QString)), this, SLOT(handleNumName(QString)), Qt::UniqueConnection);
 	connect(numLevelSpin, SIGNAL(valueChanged(int)), this, SLOT(handleNumLevel(int)), Qt::UniqueConnection);
 	connect(numFormatCombo, SIGNAL(activated(int)), this, SLOT(handleNumFormat(int)), Qt::UniqueConnection);
+	connect(numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(handleNumAlignment(int)), Qt::UniqueConnection);
 	connect(numPrefix, SIGNAL(textChanged(QString)), this, SLOT(handleNumPrefix(QString)), Qt::UniqueConnection);
 	connect(numSuffix, SIGNAL(textChanged(QString)), this, SLOT(handleNumSuffix(QString)), Qt::UniqueConnection);
 	connect(numStart, SIGNAL(valueChanged(int)), this, SLOT(handleNumStart(int)), Qt::UniqueConnection);
@@ -300,6 +323,7 @@ void PropertyWidget_ParEffect::disconnectSignals()
 	disconnect(numComboBox, SIGNAL(textActivated(QString)), this, SLOT(handleNumName(QString)));
 	disconnect(numLevelSpin, SIGNAL(valueChanged(int)), this, SLOT(handleNumLevel(int)));
 	disconnect(numFormatCombo, SIGNAL(activated(int)), this, SLOT(handleNumFormat(int)));
+	disconnect(numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(handleNumAlignment(int)));
 	disconnect(numPrefix, SIGNAL(textChanged(QString)), this, SLOT(handleNumPrefix(QString)));
 	disconnect(numSuffix, SIGNAL(textChanged(QString)), this, SLOT(handleNumSuffix(QString)));
 	disconnect(numStart, SIGNAL(valueChanged(int)), this, SLOT(handleNumStart(int)));
@@ -387,6 +411,7 @@ void PropertyWidget_ParEffect::handleParEffectUse()
 		newStyle.setHasNum(true);
 		newStyle.setNumName(numComboBox->currentText());
 		newStyle.setNumFormat(numFormatCombo->currentFormat());
+		newStyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(numAlignmentCombo->currentData().toInt()));
 		newStyle.setNumLevel(numLevelSpin->value() - 1);
 		newStyle.setNumStart(numStart->value());
 		newStyle.setNumPrefix(numPrefix->text());
@@ -519,6 +544,15 @@ void PropertyWidget_ParEffect::handleNumSuffix(const QString& suffix)
 	handleChanges(m_item, newStyle);
 }
 
+void PropertyWidget_ParEffect::handleNumAlignment(int)
+{
+	if (!m_doc || !m_item)
+		return;
+	ParagraphStyle newStyle;
+	newStyle.setNumAlignment(static_cast<ParagraphStyle::NumAlignment>(numAlignmentCombo->currentData().toInt()));
+	handleChanges(m_item, newStyle);
+}
+
 void PropertyWidget_ParEffect::handleNumStart(int start)
 {
 	if (!m_doc || !m_item)
@@ -587,6 +621,7 @@ void PropertyWidget_ParEffect::handleChanges(PageItem *item, ParagraphStyle &new
 void PropertyWidget_ParEffect::languageChange()
 {
 	fillPECombo();
+	fillNumAlignmentCombo();
 	retranslateUi(this);
 }
 
diff --git a/scribus/ui/propertywidget_pareffect.h b/scribus/ui/propertywidget_pareffect.h
index 62729f7..b66894b 100644
--- a/scribus/ui/propertywidget_pareffect.h
+++ b/scribus/ui/propertywidget_pareffect.h
@@ -64,6 +64,7 @@ public slots:
 	void handleNumLevel(int);
 	void handleNumPrefix(const QString&);
 	void handleNumSuffix(const QString&);
+	void handleNumAlignment(int);
 	void handleNumStart(int);
 	void handlePEOffset(double);
 	void handlePEIndent(bool);
@@ -78,6 +79,7 @@ private:
 	void closeEnhanced(bool show = false);
 	void setType(int id);
 	void fillBulletStrEditCombo();
+	void fillNumAlignmentCombo();
 	void fillPECombo();
 
 signals:
diff --git a/scribus/ui/propertywidget_pareffectbase.ui b/scribus/ui/propertywidget_pareffectbase.ui
index 1a2670a..d37a7ce 100644
--- a/scribus/ui/propertywidget_pareffectbase.ui
+++ b/scribus/ui/propertywidget_pareffectbase.ui
@@ -371,6 +371,30 @@
          </property>
         </widget>
        </item>
+
+       <item row="4" column="0">
+        <widget class="QLabel" name="numAlignmentLabel">
+         <property name="toolTip">
+          <string>Align the generated list marker inside its marker area</string>
+         </property>
+         <property name="text">
+          <string>Marker alignment:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+         </property>
+         <property name="buddy">
+          <cstring>numAlignmentCombo</cstring>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="1" colspan="4">
+        <widget class="QComboBox" name="numAlignmentCombo">
+         <property name="toolTip">
+          <string>Use Suffix to align suffixes such as dots or parentheses in 1., 10., 100.</string>
+         </property>
+        </widget>
+       </item>
       </layout>
      </widget>
     </widget>
@@ -552,6 +576,7 @@
   <tabstop>peCombo</tabstop>
   <tabstop>numComboBox</tabstop>
   <tabstop>numFormatCombo</tabstop>
+  <tabstop>numAlignmentCombo</tabstop>
   <tabstop>numLevelSpin</tabstop>
   <tabstop>numStart</tabstop>
   <tabstop>numPrefix</tabstop>
diff --git a/scribus/ui/smpstylewidget.cpp b/scribus/ui/smpstylewidget.cpp
index 12fde44..e3f0b30 100644
--- a/scribus/ui/smpstylewidget.cpp
+++ b/scribus/ui/smpstylewidget.cpp
@@ -6,6 +6,7 @@ for which a new license (GPL+exception) is in place.
 */
 
 #include <QEvent>
+#include <QSignalBlocker>
 
 
 #include "iconmanager.h"
@@ -13,6 +14,7 @@ for which a new license (GPL+exception) is in place.
 #include "smpstylewidget.h"
 #include "scribus.h"
 #include "scribusapp.h"
+#include "styles/paragraphstyle.h"
 #include "units.h"
 #include "util.h"
 #include "ui/charselectenhanced.h"
@@ -70,6 +72,7 @@ SMPStyleWidget::SMPStyleWidget(ScribusDoc* doc, StyleSet<CharStyle> *cstyles) :
 	numSuffix->setMaxLength(5);
 	numSuffix->setMaximumWidth(QFontMetrics(numSuffix->font()).averageCharWidth() * 7);
 	fillNumRestartCombo();
+	fillNumAlignmentCombo();
 	dropCapLines->setMinimum(2);
 	dropCapLines->setMaximum(99);
 
@@ -152,6 +155,7 @@ void SMPStyleWidget::languageChange()
 	int  oldNumRestartIndex = numRestartCombo->currentIndex();
 	bool numRestartComboBlocked = numRestartCombo->blockSignals(true);
 	fillNumRestartCombo();
+	fillNumAlignmentCombo();
 	numRestartCombo->setCurrentIndex(oldNumRestartIndex);
 	numRestartCombo->blockSignals(numRestartComboBlocked);
 
@@ -320,6 +324,8 @@ void SMPStyleWidget::show(ParagraphStyle *pstyle, QList<ParagraphStyle> &pstyles
 			numComboBox->setParentItem(0);
 		numFormatCombo->setCurrentFormat((NumFormat) pstyle->numFormat());
 		numFormatCombo->setParentFormat((NumFormat) parent->numFormat());
+		numAlignmentCombo->setCurrentItemByData(static_cast<int>(pstyle->numAlignment()), pstyle->isInhNumAlignment());
+		numAlignmentCombo->setParentItem(numAlignmentCombo->getItemIndexForData(static_cast<int>(parent->numAlignment())));
 		numLevelSpin->setValue(pstyle->numLevel() +1, pstyle->isInhNumLevel());
 		NumStruct * numS = m_Doc->numerations.value(pstyle->numName());
 		if (numS)
@@ -369,6 +375,7 @@ void SMPStyleWidget::show(ParagraphStyle *pstyle, QList<ParagraphStyle> &pstyles
 		numComboBox->setCurrentItem(numComboBox->findText(numName));
 		numNewLineEdit->clear();
 		numFormatCombo->setCurrentFormat((NumFormat) pstyle->numFormat());
+		numAlignmentCombo->setCurrentItemByData(static_cast<int>(pstyle->numAlignment()));
 		numLevelSpin->setValue(pstyle->numLevel()+1);
 		NumStruct * numS = m_Doc->numerations.value(pstyle->numName());
 		if (numS)
@@ -981,6 +988,21 @@ void SMPStyleWidget::fillNumerationsCombo()
 	numComboBox->setCurrentItem(0);
 }
 
+void SMPStyleWidget::fillNumAlignmentCombo()
+{
+	QSignalBlocker sb(numAlignmentCombo);
+	int currentData = numAlignmentCombo->currentData().toInt();
+	numAlignmentCombo->clear();
+	numAlignmentCombo->addItem(tr("Left"), static_cast<int>(ParagraphStyle::NumLeftAligned));
+	numAlignmentCombo->addItem(tr("Center"), static_cast<int>(ParagraphStyle::NumCentered));
+	numAlignmentCombo->addItem(tr("Right"), static_cast<int>(ParagraphStyle::NumRightAligned));
+	numAlignmentCombo->addItem(tr("Suffix"), static_cast<int>(ParagraphStyle::NumSuffixAligned));
+	int index = numAlignmentCombo->findData(currentData);
+	if (index < 0)
+		index = 0;
+	numAlignmentCombo->setCurrentIndex(index);
+}
+
 void SMPStyleWidget::fillNumRestartCombo()
 {
 	numRestartCombo->clear();
@@ -1095,6 +1117,16 @@ void SMPStyleWidget::showBullet(const QList<ParagraphStyle *> &pstyles, const QL
 
 void SMPStyleWidget::showNumeration(const QList<ParagraphStyle *> &pstyles, const QList<CharStyle> &cstyles, int unitIndex)
 {
+	ParagraphStyle::NumAlignment alignment = pstyles[0]->numAlignment();
+	for (int i = 0; i < pstyles.count(); ++i)
+	{
+		if (alignment != pstyles[i]->numAlignment())
+		{
+			alignment = ParagraphStyle::NumLeftAligned;
+			break;
+		}
+	}
+	numAlignmentCombo->setCurrentItemByData(static_cast<int>(alignment));
 
 	QString prefix = pstyles[0]->numPrefix();
 	for (int i = 0; i < pstyles.count(); ++i)
diff --git a/scribus/ui/smpstylewidget.h b/scribus/ui/smpstylewidget.h
index c985d70..9c7e55d 100644
--- a/scribus/ui/smpstylewidget.h
+++ b/scribus/ui/smpstylewidget.h
@@ -50,6 +50,7 @@ private:
 
 	void fillBulletStrEditCombo();
 	void fillNumerationsCombo();
+	void fillNumAlignmentCombo();
 	void fillNumRestartCombo();
 	void fillPECombo();
 	void setParagraphEffect(int);
diff --git a/scribus/ui/smpstylewidget.ui b/scribus/ui/smpstylewidget.ui
index 8a6d90e..405882e 100644
--- a/scribus/ui/smpstylewidget.ui
+++ b/scribus/ui/smpstylewidget.ui
@@ -1476,6 +1476,30 @@ A value of 0 means unlimited hyphenations.</string>
                </layout>
               </widget>
              </item>
+
+             <item row="6" column="0">
+              <widget class="QLabel" name="numAlignmentLabel">
+               <property name="toolTip">
+                <string>Align the generated list marker inside its marker area</string>
+               </property>
+               <property name="text">
+                <string>Marker alignment:</string>
+               </property>
+               <property name="alignment">
+                <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+               </property>
+               <property name="buddy">
+                <cstring>numAlignmentCombo</cstring>
+               </property>
+              </widget>
+             </item>
+             <item row="6" column="1" colspan="4">
+              <widget class="SMScComboBox" name="numAlignmentCombo">
+               <property name="toolTip">
+                <string>Use Suffix to align suffixes such as dots or parentheses in 1., 10., 100.</string>
+               </property>
+              </widget>
+             </item>
             </layout>
            </item>
           </layout>
@@ -1802,6 +1826,7 @@ A value of 0 means unlimited hyphenations.</string>
   <tabstop>numNewLineEdit</tabstop>
   <tabstop>numStartSpin</tabstop>
   <tabstop>numFormatCombo</tabstop>
+  <tabstop>numAlignmentCombo</tabstop>
   <tabstop>numLevelSpin</tabstop>
   <tabstop>bulletStrEdit</tabstop>
   <tabstop>bulletCharTableButton</tabstop>
diff --git a/scribus/ui/smtextstyles.cpp b/scribus/ui/smtextstyles.cpp
index b021322..2a1afbc 100644
--- a/scribus/ui/smtextstyles.cpp
+++ b/scribus/ui/smtextstyles.cpp
@@ -517,6 +517,7 @@ void SMParagraphStyle::setupConnections()
 	connect(m_pwidget->numComboBox, SIGNAL(textActivated(QString)), this, SLOT(slotNumName(QString)));
 	connect(m_pwidget->numLevelSpin, SIGNAL(valueChanged(int)), this, SLOT(slotNumLevel(int)));
 	connect(m_pwidget->numFormatCombo, SIGNAL(activated(int)), this, SLOT(slotNumFormat(int)));
+	connect(m_pwidget->numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(slotNumAlignment(int)));
 	connect(m_pwidget->numStartSpin, SIGNAL(valueChanged(int)), this, SLOT(slotNumStart(int)));
 	connect(m_pwidget->numRestartCombo, SIGNAL(activated(int)), this, SLOT(slotNumRestart(int)));
 	connect(m_pwidget->numRestartOtherBox, SIGNAL(toggled(bool)), this, SLOT(slotNumOther(bool)));
@@ -611,6 +612,7 @@ void SMParagraphStyle::removeConnections()
 	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)));
+	disconnect(m_pwidget->numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(slotNumAlignment(int)));
 	disconnect(m_pwidget->numLevelSpin, SIGNAL(valueChanged(int)), this, SLOT(slotNumLevel(int)));
 	disconnect(m_pwidget->numStartSpin, SIGNAL(valueChanged(int)), this, SLOT(slotNumStart(int)));
 	disconnect(m_pwidget->numRestartCombo, SIGNAL(activated(int)), this, SLOT(slotNumRestart(int)));
@@ -1086,6 +1088,23 @@ void SMParagraphStyle::slotNumFormat(int)
 	slotSelectionDirty();
 }
 
+void SMParagraphStyle::slotNumAlignment(int)
+{
+	if (m_pwidget->numAlignmentCombo->useParentValue())
+	{
+		for (int i = 0; i < m_selection.count(); ++i)
+			m_selection[i]->resetNumAlignment();
+	}
+	else
+	{
+		ParagraphStyle::NumAlignment alignment = static_cast<ParagraphStyle::NumAlignment>(m_pwidget->numAlignmentCombo->currentData().toInt());
+		for (int i = 0; i < m_selection.count(); ++i)
+			m_selection[i]->setNumAlignment(alignment);
+	}
+
+	slotSelectionDirty();
+}
+
 void SMParagraphStyle::slotNumLevel(int level)
 {
 	if (m_pwidget->numLevelSpin->useParentValue())
diff --git a/scribus/ui/smtextstyles.h b/scribus/ui/smtextstyles.h
index 3dd0235..15a9d0c 100644
--- a/scribus/ui/smtextstyles.h
+++ b/scribus/ui/smtextstyles.h
@@ -84,6 +84,7 @@ private slots:
 	void slotNumNew();
 	void slotSelectionDirty();
 	void slotNumFormat(int);
+	void slotNumAlignment(int);
 	void slotNumLevel(int level);
 	void slotNumPrefix(const QString &str);
 	void slotNumSuffix(const QString &str);
diff --git a/scribus/ui/stylemanager.cpp b/scribus/ui/stylemanager.cpp
index a0b22bc..6811964 100644
--- a/scribus/ui/stylemanager.cpp
+++ b/scribus/ui/stylemanager.cpp
@@ -181,6 +181,35 @@ void StyleManager::unitChange()
 	}
 }
 
+void StyleManager::startup()
+{
+	setFontSize();
+	if (!m_doc && !isModal())
+	{
+		storeVisibility(false);
+		hide();
+		emit paletteShown(false);
+		return;
+	}
+
+	ScrPaletteBase::startup();
+}
+
+void StyleManager::setPaletteShown(bool visible)
+{
+	if (visible && !m_doc && !isModal())
+	{
+		storeVisibility(false);
+		hide();
+		emit paletteShown(false);
+		return;
+	}
+
+	ScrPaletteBase::setPaletteShown(visible);
+	if (!visible)
+		emit paletteShown(false);
+}
+
 template<class ItemType> 
 ItemType* StyleManager::item()
 {
@@ -254,6 +283,8 @@ void StyleManager::setDoc(ScribusDoc *doc)
 	deleteUnusedButton->setEnabled(hasDoc);
 	m_rightClickPopup->setEnabled(hasDoc);
 	m_newPopup->setEnabled(hasDoc);
+	if (!hasDoc && !isModal())
+		setPaletteShown(false);
 	if (m_doc && (m_doc != oldDoc) && this->isVisible())
 		connect(m_doc->m_Selection, SIGNAL(selectionChanged()), this, SLOT(slotDocSelectionChanged()));
 
diff --git a/scribus/ui/stylemanager.h b/scribus/ui/stylemanager.h
index f47a98e..d50503c 100644
--- a/scribus/ui/stylemanager.h
+++ b/scribus/ui/stylemanager.h
@@ -49,10 +49,12 @@ public:
 	 * document, and recreates the tree and associated shortcut actions.
 	 */
 	void reloadStyles();
+	void startup();
 
 	QMap<QString,Keys> keyMap();
 
 public slots:
+	void setPaletteShown(bool visible) override;
 	void setDoc(ScribusDoc *doc);
 	void languageChange();
 	void unitChange();

qirat

2026-07-09 04:46

reporter   ~0053999

Here is more context and a test file (with 100 list entries).
context_list-marker-alignment.md (1,657 bytes)   
CONTEXT
## Patch: numbered-list marker alignment

This patch adds a **Marker alignment** option for numbered-list paragraph effects.

Problem: in numbered lists with suffixes such as dots, markers like `1.`, `2.`, `10.`, `100.` do not always align as expected. This is especially visible when moving from single-digit to double-/triple-digit numbering.

The patch adds this control to numbered-list settings:

```
Marker alignment: Left / Center / Right / Suffix
```

It is available in (2 places):

```
Style Manager → Paragraph Styles → Paragraph Effects → Numbered List
Properties Palette → Text → Paragraph Effects → Numbered List
```

Default remains Left, so existing documents should keep the same layout unless changed by the user.

### Behaviour

```
Left    keeps the current/default marker positioning.
Center  centers the marker inside the marker area.
Right   right-aligns the full generated marker.
Suffix  aligns markers by the suffix/end part, useful for lists like 1., 10., 100.
```

This allows users to get cleaner alignment for numbered lists while keeping existing behaviour available.

Implementation summary:

```
- Adds a ParagraphStyle property for numbered-list marker alignment.
- Saves/loads it as NumerationAlignment.
- Wires it into the Style Manager and Properties Palette paragraph-effect UI.
- Updates numbered-list layout so generated markers are positioned inside the marker area according to the selected alignment.
```

The layout change is done in the generated numbered-list marker handling, not through normal tab stops, because Scribus list markers are paragraph effects rather than ordinary typed text.
list-marker-alignment_100items_test.sla (30,974 bytes)   
<?xml version="1.0" encoding="UTF-8"?>
<SCRIBUSUTF8NEW Version="1.7.4.svn">
    <Document PageCount="2" PageWidth="576" PageHeight="828" BorderLeft="36" BorderRight="36" BorderTop="36" BorderBottom="36" MarginPreset="0" BleedTop="0" BleedLeft="0" BleedRight="0" BleedBottom="0" PageOrientation="0" PageSize="Custom" FirstPageNumber="1" PagePositioning="1" BindingDirection="0" IsRTL="0" AutomaticTextFrameColumnCount="1" AutomaticTextFrameColumnGap="11" UnitIndex="2" FontFace="EB Garamond Regular" FontSize="12" TextColumnCount="1" TextColumnGap="0" TabFillCharacter="" TabWidth="36" TextDistanceLeft="0" TextDistanceRight="0" TextDistanceBottom="0" TextDistanceTop="0" FirstLineOffset="1" DocumentInfoAuthor="" DocumentInfoComments="" DocumentInfoKeywords="" DocumentInfoPublisher="" DocumentInfoDate="" DocumentInfoType="" DocumentInfoFormat="" DocumentInfoIdentifier="" DocumentInfoSource="" DocumentInfoLanguage="" DocumentInfoRelation="" DocumentInfoCoverage="" DocumentInfoRights="" DocumentInfoContributors="" DocumentInfoTitle="" DocumentInfoSubject="" SuperScriptDisplacement="33" SuperScriptScaling="66" SubScriptDisplacement="33" SubScriptScaling="66" SmallCapsScaling="75" BaselineGridSpacing="15.6" BaselineGridOffset="0" AutoLineSpacingPct="100" UnderlinePosition="-1" UnderlineWidth="-1" StrikeThruPosition="-1" StrikeThruWidth="-1" GroupCounter="1" DocumentHasCMS="0" SoftProofOn="0" SoftProofFullOn="0" ColorManagementActive="0" GamutCheck="0" BlackPoint="1" DefaultPrinterProfile="ISO Coated v2 300% (basICColor)" DefaultImageRGBProfile="sRGB display profile (ICC v2.2)" DefaultImageCMYKProfile="ISO Coated v2 300% (basICColor)" DefaultSolidColorRGBProfile="sRGB display profile (ICC v2.2)" DefaultSolidColorCMYKProfile="ISO Coated v2 300% (basICColor)" DefaultIntentColors="1" DefaultIntentImages="0" ActiveLayer="0" Language="en_GB" AutomaticHyphenation="1" AutomaticHyphenationCheck="1" GuideLock="0" SnapToGuides="0" SnapToGrid="0" SnapToItems="0" MinorGridSpacing="18" MajorGridSpacing="72" ShowGrid="0" ShowGuides="1" ShowColumnBorders="1" ShowFrames="1" ShowControls="0" ShowTableCellFrames="0" ShowLayerMarkers="0" ShowMargins="1" ShowBaselineGrid="0" ShowImages="1" ShowLinks="0" RulerMode="1" ShowRulers="1" ShowBleed="1" RulerXOffset="0" RulerYOffset="0" GuideRadius="10" GrabRadius="4" PolygonCorners="4" PolygonFactor="0.502" PolygonRotation="0" PolygonInnerRotation="0" PolygonCurvature="0" PolygonOuterCurvature="0" PolygonUseFactor="0" ArcStartAngle="30" ArcSweepAngle="300" SpiralStartAngle="0" SpiralEndAngle="1080" SpiralFactor="1.2" AutoSave="1" AutoSaveTime="600000" AutoSaveCount="1" AutoSaveKeep="0" AutoSaveInDocumentDirectory="0" AutoSaveDirectory="/home/buttar/x/xdump" ScratchBottom="18" ScratchLeft="108" ScratchRight="108" ScratchTop="18" GapHorizontal="0" GapVertical="36" LineStartArrow="0" LineEndArrow="0" ShapeLineColor="None" ShapeFillColor="Cyan" LineColor="Black" TextColor="Black" TextStrokeColor="Black" TextBackgroundColor="None" TextLineColor="None" TextBackgroundColorShade="100" TextLineColorShade="100" TextColorShade="100" TextStrokeColorShade="100" ShapeLineStyle="1" LineStyle="1" ShapeLineWidth="1" LineWidth="1" ShapeLineColorShade="100" LineColorShade="100" ShapeFillColorShade="100" ImageFillColor="None" ImageFillColorShade="100" ImageStrokeColor="None" ImageStrokeColorShade="100" ImageScaleX="1" ImageScaleY="1" ImageScaleType="1" ImageAspectRatio="1" ImageUseEmbeddedPath="0" ImageLowResType="1" DisplayOffsetX="9" DisplayOffsetY="9" RotationConstrainAngle="15" MinorGridColor="#00ff00" MajorGridColor="#00ff00" GuidesColor="#000080" BaselineGridColor="#c0c0c0" RenderStack="2 1 4 3 0" GridType="0" PageColor="#ffffff" MarginColor="#813d9c" ShowMarginsFilled="0" CurrentProfile="PDF 1.6" CalligraphicPenFillColor="Black" CalligraphicPenLineColor="Black" CalligraphicPenFillColorShade="100" CalligraphicPenLineColorShade="100" CalligraphicPenLineWidth="1" CalligraphicPenAngle="0" CalligraphicPenWidth="10" CalligraphicPenStyle="1">
        <CheckProfile Name="PDF 1.3" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="0" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="0" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="0" CheckFontNotEmbedded="1" CheckFontIsOpenType="1" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="0"/>
        <CheckProfile Name="PDF 1.4" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="0" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="0" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="0" CheckFontNotEmbedded="1" CheckFontIsOpenType="1" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <CheckProfile Name="PDF 1.5" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="0" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="0" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="0" CheckFontNotEmbedded="1" CheckFontIsOpenType="1" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <CheckProfile Name="PDF 1.6" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="0" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="0" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="0" CheckFontNotEmbedded="1" CheckFontIsOpenType="0" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <CheckProfile Name="PDF/X-1a" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="1" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="1" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="1" CheckDeviceColorsAndOutputIntent="0" CheckFontNotEmbedded="1" CheckFontIsOpenType="1" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <CheckProfile Name="PDF/X-3" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="1" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="1" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="1" CheckFontNotEmbedded="1" CheckFontIsOpenType="1" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <CheckProfile Name="PDF/X-4" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="0" MinimumResolution="299" MaximumResolution="600" CheckAnnotations="1" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="1" CheckFontNotEmbedded="1" CheckFontIsOpenType="0" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <CheckProfile Name="PostScript" IgnoreErrors="0" AutoCheck="1" CheckGlyphs="1" CheckOrphans="1" CheckOverflow="1" CheckPictures="1" CheckPartFilledImageFrames="0" CheckResolution="1" CheckTransparency="1" MinimumResolution="144" MaximumResolution="2400" CheckAnnotations="0" CheckRasterPDF="1" CheckForGIF="1" IgnoreOffLayers="0" CheckNotCMYKOrSpot="0" CheckDeviceColorsAndOutputIntent="0" CheckFontNotEmbedded="0" CheckFontIsOpenType="0" CheckAppliedMasterDifferentSide="1" CheckEmptyTextFrames="1" CheckImageHasProgressiveEncoding="1"/>
        <Color Name="Black" Space="CMYK" C="0" M="0" Y="0" K="100"/>
        <Color Name="Blue" Space="RGB" R="0" G="0" B="255"/>
        <Color Name="Cool Black" Space="CMYK" C="60" M="0" Y="0" K="100"/>
        <Color Name="Cyan" Space="CMYK" C="100" M="0" Y="0" K="0"/>
        <Color Name="Green" Space="RGB" R="0" G="255" B="0"/>
        <Color Name="Magenta" Space="CMYK" C="0" M="100" Y="0" K="0"/>
        <Color Name="Red" Space="RGB" R="255" G="0" B="0"/>
        <Color Name="Registration" Space="CMYK" C="100" M="100" Y="100" K="100" Register="1"/>
        <Color Name="Rich Black" Space="CMYK" C="60" M="40" Y="40" K="100"/>
        <Color Name="Warm Black" Space="CMYK" C="0" M="60" Y="29.8039215686275" K="100"/>
        <Color Name="White" Space="CMYK" C="0" M="0" Y="0" K="0"/>
        <Color Name="Yellow" Space="CMYK" C="0" M="0" Y="100" K="0"/>
        <Hyphenator/>
        <CharacterStyle Name="Default Character Style" DefaultStyle="1" Font="EB Garamond Regular" FontSize="12" FontFeatures="" Features="inherit" FontColor="Black" FillShade="100" HyphenWordMinimum="3" StrokeColor="Black" BackgroundColor="None" BackgroundShade="100" StrokeShade="100" TextShadowXOffset="5" TextShadowYOffset="-5" TextOutlineWidth="1" TextUnderlineOffset="-0.1" TextUnderlineWidth="-0.1" TextStrikeThroughOffset="-0.1" TextStrikeThroughWidth="-0.1" ScaleHorizontal="100" ScaleVertical="100" BaselineOffset="0" Kerning="0" Language="en_GB"/>
        <ParagraphStyle Name="Default Paragraph Style" DefaultStyle="1" Alignment="0" Direction="0" LineSpacingMode="0" LineSpacing="15" LeftMargin="0" RightMargin="0" FirstIndent="18" GapBefore="0" GapAfter="0" ParagraphEffectOffset="0" HasDropCap="0" DropCapLines="2" Bullet="0" Numeration="0" HyphenConsecutiveLines="2" ParagraphBackgroundColor="None" ParagraphBackgroundColorShade="100"/>
        <ParagraphStyle Name="LIST_Montserrat" FirstIndent="0" ParagraphEffectOffset="9" HasDropCap="0" Bullet="0" Numeration="1" NumerationAlignment="3" OpticalMarginSetId="preset_0" Font="Montserrat Regular" FontFeatures="-clig" Language="en_GB"/>
        <ParagraphStyle Name="LIST_Lato" FirstIndent="0" ParagraphEffectOffset="9" HasDropCap="0" Bullet="0" Numeration="1" NumerationAlignment="3" NumerationSuffix="." OpticalMarginSetId="preset_0" Font="Lato Regular" FontFeatures="-clig" Language="en_GB"/>
        <ParagraphStyle Name="LIST_Garamond" FirstIndent="0" ParagraphEffectOffset="9" HasDropCap="0" Bullet="0" Numeration="1" NumerationAlignment="3" OpticalMarginSetId="preset_0" Font="Garamond Regular" FontFeatures="-liga,-clig" Language="en_GB"/>
        <ParagraphStyle Name="LIST_EB_Garamond" FirstIndent="0" ParagraphEffectOffset="9" HasDropCap="0" Bullet="0" BulletStr="•" Numeration="1" NumerationAlignment="3" NumerationPrefix="" NumerationSuffix="." NumerationOther="0" OpticalMarginSetId="preset_0" Font="EB Garamond Regular" FontFeatures="-clig" Language="en_GB"/>
        <ParagraphStyle Name="LIST_Cor_Garamond" FirstIndent="0" ParagraphEffectOffset="9" HasDropCap="0" Bullet="0" Numeration="1" NumerationAlignment="3" OpticalMarginSetId="preset_0" Font="Cormorant Garamond Regular" FontFeatures="-clig" Language="en_GB"/>
        <TableStyle Name="Default Table Style" DefaultStyle="1" FillColor="None" FillShade="100">
            <TableBorderLeft>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderLeft>
            <TableBorderRight>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderRight>
            <TableBorderTop>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderTop>
            <TableBorderBottom>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderBottom>
        </TableStyle>
        <CellStyle Name="Default Cell Style" DefaultStyle="1" FillColor="None" FillShade="100" LeftPadding="1" RightPadding="1" TopPadding="1" BottomPadding="1">
            <TableBorderLeft>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderLeft>
            <TableBorderRight>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderRight>
            <TableBorderTop>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderTop>
            <TableBorderBottom>
                <TableBorderLine Width="1" PenStyle="1" Color="Black" Shade="100"/>
            </TableBorderBottom>
        </CellStyle>
        <Layers Number="0" Level="0" Name="Background" IsViewable="1" IsPrintable="1" IsEditable="1" IsSelectable="0" FlowControl="1" Transparency="1" BlendMode="0" OutlineMode="0" LayerColor="#000000"/>
        <Printer firstUse="1" toFile="0" useAltPrintCommand="0" outputSeparations="0" useSpotColors="1" useColor="1" mirrorH="0" mirrorV="0" useICC="0" doGCR="0" doClip="0" setDevParam="0" useDocBleeds="1" cropMarks="0" bleedMarks="0" registrationMarks="0" colorMarks="0" includePDFMarks="1" PSLevel="3" PrintEngine="3" markLength="20.0016" markOffset="0" BleedTop="0" BleedLeft="0" BleedRight="0" BleedBottom="0" printer="File" filename="" separationName="All" printerCommand=""/>
        <PDF firstUse="1" Thumbnails="0" Articles="0" Bookmarks="0" Compress="1" CMethod="0" Quality="0" EmbedPDF="0" MirrorH="0" MirrorV="0" Clip="0" rangeSel="0" rangeTxt="" RotateDeg="0" PresentMode="0" RecalcPic="1" FontEmbedding="0" Grayscale="0" RGBMode="0" UseProfiles="0" UseProfiles2="0" Binding="0" PicRes="600" Resolution="300" Version="16" Intent="1" Intent2="0" SolidP="sRGB display profile (ICC v2.2)" ImageP="sRGB display profile (ICC v2.2)" PrintP="ISO Coated v2 300% (basICColor)" InfoString="" BTop="0" BLeft="0" BRight="0" BBottom="0" useDocBleeds="1" cropMarks="0" bleedMarks="0" registrationMarks="0" colorMarks="0" docInfoMarks="0" markLength="18" markOffset="0" ImagePr="0" PassOwner="" PassUser="" Permissions="-4" Encrypt="0" UseLayers="0" UseLpi="0" UseSpotColors="1" doMultiFile="0" displayBookmarks="0" displayFullscreen="0" displayLayers="0" displayThumbs="0" hideMenuBar="0" hideToolBar="0" fitWindow="0" openAfterExport="0" PageLayout="0" openAction="">
            <LPI Color="" Frequency="0" Angle="0" SpotFunction="0"/>
            <LPI Color="Black" Frequency="133" Angle="45" SpotFunction="3"/>
            <LPI Color="Cyan" Frequency="133" Angle="105" SpotFunction="3"/>
            <LPI Color="Magenta" Frequency="133" Angle="75" SpotFunction="3"/>
            <LPI Color="Yellow" Frequency="133" Angle="90" SpotFunction="3"/>
        </PDF>
        <DocItemAttributes/>
        <Indexes/>
        <TablesOfContents/>
        <NotesStyles>
            <notesStyle Name="Default" Start="1" Endnotes="0" Type="Type_1_2_3" Range="0" Prefix="" Suffix=")" AutoHeight="1" AutoWidth="1" AutoRemove="1" AutoWeld="1" SuperNote="1" SuperMaster="1" MarksStyle="" NotesStyle=""/>
        </NotesStyles>
        <OpticalMarginSets>
            <Set Id="preset_0" Type="preset" Name="Default">
                <Rules>
                    <Rule Left="0" Right="0.5" Unit="7" Characters="U+003A,U+003B,U+2013,U+2033"/>
                    <Rule Left="0" Right="0.75" Unit="7" Characters="U+002C,U+002D,U+002E,U+2010,U+2032"/>
                    <Rule Left="0.25" Right="0.25" Unit="7" Characters="U+2014"/>
                    <Rule Left="0.5" Right="0" Unit="7" Characters="U+2036"/>
                    <Rule Left="0.5" Right="0.5" Unit="7" Characters="U+0022,U+0028,U+0029,U+005B,U+005D,U+00AB,U+00BB,U+201C,U+201D,U+201E,U+201F"/>
                    <Rule Left="0.75" Right="0" Unit="7" Characters="U+2035"/>
                    <Rule Left="0.75" Right="0.75" Unit="7" Characters="U+0027,U+002A,U+0060,U+007E,U+00B4,U+2018,U+2019,U+201A,U+201B,U+2039,U+203A"/>
                </Rules>
            </Set>
        </OpticalMarginSets>
        <PageSets>
            <Set Name="Single Page" FirstPage="0" Rows="1" Columns="1"/>
            <Set Name="Facing Pages" FirstPage="1" Rows="1" Columns="2">
                <PageNames Name="Left Page"/>
                <PageNames Name="Right Page"/>
            </Set>
            <Set Name="3-Fold" FirstPage="0" Rows="1" Columns="3">
                <PageNames Name="Left Page"/>
                <PageNames Name="Middle"/>
                <PageNames Name="Right Page"/>
            </Set>
            <Set Name="4-Fold" FirstPage="0" Rows="1" Columns="4">
                <PageNames Name="Left Page"/>
                <PageNames Name="Middle Left"/>
                <PageNames Name="Middle Right"/>
                <PageNames Name="Right Page"/>
            </Set>
        </PageSets>
        <Sections>
            <Section Number="0" Name="Section 1" From="0" To="1" Type="Type_1_2_3" Start="1" Reversed="0" Active="1" FillChar="0" FieldWidth="0"/>
        </Sections>
        <MasterPage PageXPosition="108" PageYPosition="18" PageWidth="576" PageHeight="828" BorderLeft="36" BorderRight="36" BorderTop="36" BorderBottom="36" PageNumber="0" PageName="Normal Left" MasterPageName="" Size="Custom" Orientation="0" LeftPage="1" Preset="0" VerticalGuides="" HorizontalGuides="" AutoGuideHorizontalGap="0" AutoGuideVerticalGap="0" AutoGuideHorizontalCount="0" AutoGuideVerticalCount="0" AutoGuideHorizontalReference="0" AutoGuideVerticalReference="0" AutoGuideSelection="0 0 0 0" PageEffectDuration="1" PageViewDuration="1" PageEffectType="0" PageEffectLineDirection="0" PageEffectInsideOutside="0" PageEffectDirection="0"/>
        <MasterPage PageXPosition="108" PageYPosition="18" PageWidth="576" PageHeight="828" BorderLeft="36" BorderRight="36" BorderTop="36" BorderBottom="36" PageNumber="1" PageName="Normal Right" MasterPageName="" Size="Custom" Orientation="0" LeftPage="0" Preset="0" VerticalGuides="" HorizontalGuides="" AutoGuideHorizontalGap="0" AutoGuideVerticalGap="0" AutoGuideHorizontalCount="0" AutoGuideVerticalCount="0" AutoGuideHorizontalReference="0" AutoGuideVerticalReference="0" AutoGuideSelection="0 0 0 0" PageEffectDuration="1" PageViewDuration="1" PageEffectType="0" PageEffectLineDirection="0" PageEffectInsideOutside="0" PageEffectDirection="0"/>
        <Page PageXPosition="684" PageYPosition="18" PageWidth="576" PageHeight="828" BorderLeft="36" BorderRight="36" BorderTop="36" BorderBottom="36" PageNumber="0" PageName="" MasterPageName="Normal Right" Size="Custom" Orientation="0" LeftPage="0" Preset="0" VerticalGuides="" HorizontalGuides="" AutoGuideHorizontalGap="0" AutoGuideVerticalGap="0" AutoGuideHorizontalCount="0" AutoGuideVerticalCount="0" AutoGuideHorizontalReference="0" AutoGuideVerticalReference="0" AutoGuideSelection="0 0 0 0" PageEffectDuration="1" PageViewDuration="1" PageEffectType="0" PageEffectLineDirection="0" PageEffectInsideOutside="0" PageEffectDirection="0"/>
        <Page PageXPosition="108" PageYPosition="882" PageWidth="576" PageHeight="828" BorderLeft="36" BorderRight="36" BorderTop="36" BorderBottom="36" PageNumber="1" PageName="" MasterPageName="Normal Left" Size="Custom" Orientation="0" LeftPage="0" Preset="0" VerticalGuides="" HorizontalGuides="" AutoGuideHorizontalGap="0" AutoGuideVerticalGap="0" AutoGuideHorizontalCount="0" AutoGuideVerticalCount="0" AutoGuideHorizontalReference="0" AutoGuideVerticalReference="0" AutoGuideSelection="0 0 0 0" PageEffectDuration="1" PageViewDuration="1" PageEffectType="0" PageEffectLineDirection="0" PageEffectInsideOutside="0" PageEffectDirection="0"/>
        <PageObject XPosition="720" YPosition="54" OwnPage="0" ItemID="1197331536" ItemType="4" Width="504" Height="756" FrameType="0" ClipEdited="0" LineWidth="1" LinePenStyle="1" ImageScaleX="1" ImageScaleY="1" ImageOffsetX="0" ImageOffsetY="0" ImageRotation="0" ImageVisible="1" ImageScaleType="1" ImageRatio="1" Columns="1" ColumnGap="0" AutomaticTextFrame="0" TextToFrameDistanceLeft="0" TextToFrameDistanceTop="0" TextToFrameDistanceBottom="0" TextToFrameDistanceRight="0" VerticalAlignment="0" FirstLineOffset="1" PathTextShowPath="0" PathTextDistanceFromPath="0" TextPathType="0" TextPathFlipped="0" Path="M0 0 L504 0 L504 756 L0 756 L0 0 Z" ContourLinePath="M0 0 L504 0 L504 756 L0 756 L0 0 Z" gXpos="720" gYpos="54" gWidth="0" gHeight="0" ParagraphStyle="LIST_EB_Garamond" Layer="0" NextItem="797831277" BackItem="-1">
            <StoryText>
                <DefaultStyle Parent="LIST_EB_Garamond"/>
                <Content Chars="Pencil"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Mountain"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Teapot"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Bicycle"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Cloud"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Lantern"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Notebook"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Apple"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Compass"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Window"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="River"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Button"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Candle"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Backpack"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Mirror"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Chair"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Keyboard"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Spoon"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Radio"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Blanket"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Pebble"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Clock"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Umbrella"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Kite"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Basket"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Violin"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Map"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Cup"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Door"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Feather"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Camera"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Lemon"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Rope"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Train"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Pillow"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Shell"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Coin"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Paintbrush"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Ladder"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Tomato"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Wallet"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Guitar"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Planet"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Scarf"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Book"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Tree"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Mug"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Castle"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Sock"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Anchor"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Plate"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Horse"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Candleholder"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Drum"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Bridge"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Orange"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Vase"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Telescope"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Garden"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Hat"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Stone"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Bell"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Sofa"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Envelope"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Pumpkin"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Bottle"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Rocket"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Lamp"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Glove"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Island"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Fork"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Toothbrush"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Robot"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Carpet"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Leaf"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Hammer"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Ice cube"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Tent"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Necklace"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Suitcase"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Fan"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Puzzle"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Treehouse"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Skateboard"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Magnet"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Waterfall"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Soap"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Flute"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Flag"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Cactus"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Newspaper"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Basketball"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Jar"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Firewood"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Sunglasses"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Crane"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Balloon"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Chessboard"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Lighthouse"/>
                <para Parent="LIST_EB_Garamond"/>
                <Content Chars="Rice bowl"/>
                <trail Parent="LIST_EB_Garamond"/>
            </StoryText>
        </PageObject>
        <PageObject XPosition="144" YPosition="918" OwnPage="1" ItemID="797831277" ItemType="4" Width="504" Height="756" FrameType="0" ClipEdited="0" LineWidth="1" LinePenStyle="1" ImageScaleX="1" ImageScaleY="1" ImageOffsetX="0" ImageOffsetY="0" ImageRotation="0" ImageVisible="1" ImageScaleType="1" ImageRatio="1" Columns="1" ColumnGap="0" AutomaticTextFrame="0" TextToFrameDistanceLeft="0" TextToFrameDistanceTop="0" TextToFrameDistanceBottom="0" TextToFrameDistanceRight="0" VerticalAlignment="0" FirstLineOffset="1" PathTextShowPath="0" PathTextDistanceFromPath="0" TextPathType="0" TextPathFlipped="0" Path="M0 0 L504 0 L504 756 L0 756 L0 0 Z" ContourLinePath="M0 0 L504 0 L504 756 L0 756 L0 0 Z" gXpos="144" gYpos="918" gWidth="0" gHeight="0" ParagraphStyle="LIST_EB_Garamond" Layer="0" NextItem="-1" BackItem="1197331536"/>
    </Document>
</SCRIBUSUTF8NEW>

qirat

2026-07-11 06:27

reporter   ~0054013

Key changes:
- icon added, labels improved
- make "suffix" a default selected option
- "Suffix" option only available if "Suffix:[Box]" has a value
- the "Restart after" options moved below related property
- same reflected in the PP
2_list-marker-alignment-v1.5_layout-labels-tooltips.patch (50,205 bytes)   
diff --git a/resources/iconsets/1_7_0/16/paragraph-list-marker-alignment.svg b/resources/iconsets/1_7_0/16/paragraph-list-marker-alignment.svg
new file mode 100644
index 0000000..02d0280
--- /dev/null
+++ b/resources/iconsets/1_7_0/16/paragraph-list-marker-alignment.svg
@@ -0,0 +1,5 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><style>@import '../colors.css';</style>
+<path d="M5 2H6V14H5V2Z" class="info"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3 3H8V5H3V3ZM1 7H8V9H1V7ZM3 11H8V13H3V11Z"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10 3H15V4H10V3ZM10 5H14V6H10V5ZM10 7H15V8H10V7ZM10 9H14V10H10V9ZM10 11H15V12H10V11ZM10 13H14V14H10V13Z"/>
+</svg>
diff --git a/resources/iconsets/1_7_0/1_7_0.xml b/resources/iconsets/1_7_0/1_7_0.xml
index 6880d76..32fb3d4 100644
--- a/resources/iconsets/1_7_0/1_7_0.xml
+++ b/resources/iconsets/1_7_0/1_7_0.xml
@@ -551,6 +551,7 @@ On Dark:  #e580ff; rgb(229, 128, 255); hsl(288, 100, 75)
 		<icon id="paragraph-indent-right" file="16/paragraph-indent-right.svg" />
 		<icon id="paragraph-language" file="16/paragraph-language.svg" />
 		<icon id="paragraph-line-height" file="16/font-lineheight.svg" />
+		<icon id="paragraph-list-marker-alignment" file="16/paragraph-list-marker-alignment.svg" />
 		<icon id="paragraph-list-offset" file="16/paragraph-list-indent-left.svg" />
 		<icon id="paragraph-optical-margin-left" file="16/paragraph-optical-margin-left.svg" />
 		<icon id="paragraph-optical-margin-right" file="16/paragraph-optical-margin-right.svg" />
diff --git a/scribus/pageitem_textframe.cpp b/scribus/pageitem_textframe.cpp
index 1c5b95c..560e308 100644
--- a/scribus/pageitem_textframe.cpp
+++ b/scribus/pageitem_textframe.cpp
@@ -32,6 +32,7 @@ for which a new license (GPL+exception) is in place.
 #include <QRegion>
 #include <cairo.h>
 #include <cassert>
+#include <harfbuzz/hb.h>
 
 #include "actionmanager.h"
 #include "appmodes.h"
@@ -76,6 +77,59 @@ for which a new license (GPL+exception) is in place.
 
 using namespace std;
 
+
+static double markerSuffixWidth(const QString& suffix, const CharStyle& charStyle, ParagraphStyle::DirectionType direction)
+{
+	if (suffix.isEmpty())
+		return 0.0;
+
+	hb_font_t* hbFont = reinterpret_cast<hb_font_t*>(charStyle.font().hbFont());
+	if (!hbFont)
+		return 0.0;
+
+	hb_font_set_scale(hbFont, charStyle.fontSize(), charStyle.fontSize());
+	hb_buffer_t* buffer = hb_buffer_create();
+	hb_buffer_add_utf16(buffer, suffix.utf16(), suffix.length(), 0, suffix.length());
+	hb_buffer_set_direction(buffer, direction == ParagraphStyle::RTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
+	hb_buffer_set_language(buffer, hb_language_from_string(charStyle.language().toUtf8().constData(), -1));
+	hb_buffer_guess_segment_properties(buffer);
+	hb_shape(hbFont, buffer, nullptr, 0);
+
+	unsigned int count = 0;
+	hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, &count);
+	double advance = 0.0;
+	for (unsigned int i = 0; i < count; ++i)
+		advance += positions[i].x_advance / 10.0;
+	hb_buffer_destroy(buffer);
+
+	return advance * (charStyle.scaleH() / 1000.0);
+}
+
+static double markerAlignmentOffset(const ParagraphStyle& style, double markerWidth, const CharStyle& charStyle)
+{
+	if (!style.hasNum() || markerWidth <= 0.0)
+		return 0.0;
+
+	switch (style.numAlignment())
+	{
+		case ParagraphStyle::NumAlignmentLeft:
+			return 0.0;
+		case ParagraphStyle::NumAlignmentCenter:
+			return markerWidth / 2.0;
+		case ParagraphStyle::NumAlignmentSuffix:
+		{
+			if (style.numSuffix().isEmpty())
+				return markerWidth;
+			const double suffixWidth = qBound(0.0, markerSuffixWidth(style.numSuffix(), charStyle, style.direction()), markerWidth);
+			return markerWidth - suffixWidth;
+		}
+		case ParagraphStyle::NumAlignmentRight:
+		default:
+			return markerWidth;
+	}
+}
+
+
 PageItem_TextFrame::PageItem_TextFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, const QString& fill, const QString& outline)
 	: PageItem(pa, PageItem::TextFrame, x, y, w, h, w2, fill, outline)
 {
@@ -1826,6 +1880,10 @@ void PageItem_TextFrame::layout()
 									break;
 								effectWidth += glyph.width();
 							}
+							// Alignment moves part (or all) of a numbered marker to the
+							// outside of the indent. Auto hanging indent must reserve only
+							// the marker width that remains on the text side of that anchor.
+							effectWidth -= markerAlignmentOffset(style, effectWidth, charStyle);
 
 							if (style.direction() == ParagraphStyle::RTL)
 							{
@@ -2438,12 +2496,30 @@ void PageItem_TextFrame::layout()
 					tabs.status = TabNONE;
 				}
 			}
-			if ((DropCmode || BulNumMode) && !outs)
+			bool parEffectEndsHere = true;
+			if (DropCmode || BulNumMode)
+			{
+				const int nextGlyphIndex = i + 1;
+				parEffectEndsHere = !shapedText.haveMoreText(nextGlyphIndex, glyphClusters)
+					|| glyphClusters[nextGlyphIndex].firstChar() != a;
+			}
+			if ((DropCmode || BulNumMode) && !outs && parEffectEndsHere)
 			{
-				current.xPos += style.parEffectOffset();
-				// for bulleted lists, make sure offset is applied only after last bullet char
-				// for numbered lists, make sure that offset is applied only after the suffix
-				// loop over previous current.glyphs and set their extraWidth to 0.0
+				int markerStartIndex = currentIndex;
+				double markerWidth = 0.0;
+				while (markerStartIndex > 0 && current.glyphs[markerStartIndex - 1].firstChar() == a)
+					--markerStartIndex;
+				for (int j = markerStartIndex; j <= currentIndex; ++j)
+					markerWidth += current.glyphs[j].width();
+
+				const double alignmentOffset = markerAlignmentOffset(style, markerWidth, charStyle);
+				const double markerShift = (style.direction() == ParagraphStyle::RTL) ? alignmentOffset : -alignmentOffset;
+				for (int j = markerStartIndex; j <= currentIndex; ++j)
+					current.glyphs[j].xoffset += markerShift;
+
+				const double trailingSpace = style.parEffectOffset() - alignmentOffset;
+				current.xPos += trailingSpace;
+				// Apply the trailing spacing only after the complete bullet/number marker.
 				for (int j = 0; j < current.glyphs.size(); j++)
 				{
 					if (j != currentIndex)
@@ -2453,14 +2529,11 @@ void PageItem_TextFrame::layout()
 						currentGlyph.extraWidth = 0.0;
 					}
 				}
-				// set the offset for Drop Cap, Bullet & Number List
-				current.glyphs[currentIndex].extraWidth += style.parEffectOffset();
-				// RTL: the line is reversed at render time, so trailing extraWidth lands on
-				// the marker's OUTER (right) edge, not between the marker and the text. Shift
-				// the marker glyph to the right of its advance box so the reserved offset
-				// falls on the inner (left) side, next to the text. Drop caps and bullet /
-				// numbered-list markers share this offset mechanism.
-				if (style.direction() == ParagraphStyle::RTL && (DropCmode || BulNumMode))
+				current.glyphs[currentIndex].extraWidth += trailingSpace;
+				// RTL rendering places trailing extraWidth on the marker's outer edge;
+				// move the final cluster by the requested marker-text gap so that the
+				// reserved space remains between the marker and paragraph text.
+				if (style.direction() == ParagraphStyle::RTL)
 					current.glyphs[currentIndex].xoffset += style.parEffectOffset();
 
 				if (DropCmode)
diff --git a/scribus/plugins/fileloader/scribus150format/scribus150format.cpp b/scribus/plugins/fileloader/scribus150format/scribus150format.cpp
index 424ddcf..a57eb12 100644
--- a/scribus/plugins/fileloader/scribus150format/scribus150format.cpp
+++ b/scribus/plugins/fileloader/scribus150format/scribus150format.cpp
@@ -3179,6 +3179,10 @@ void Scribus150Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re
 	if (attrs.hasAttribute(NumerationSuffix))
 		newStyle.setNumSuffix(attrs.valueAsString(NumerationSuffix));
 
+	static const QString NumerationAlignment("NumerationAlignment");
+	if (attrs.hasAttribute(NumerationAlignment))
+		newStyle.setNumAlignment(attrs.valueAsInt(NumerationAlignment));
+
 	static const QString NumerationRestart("NumerationRestart");
 	if (attrs.hasAttribute(NumerationRestart))
 		newStyle.setNumRestart(attrs.valueAsInt(NumerationRestart));
@@ -5523,6 +5527,8 @@ PageItem* Scribus150Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut
 		pstyle.setNumPrefix(attrs.valueAsString("NumerationPrefix"));
 	if (attrs.hasAttribute("NumerationSuffix"))
 		pstyle.setNumSuffix(attrs.valueAsString("NumerationSuffix"));
+	if (attrs.hasAttribute("NumerationAlignment"))
+		pstyle.setNumAlignment(attrs.valueAsInt("NumerationAlignment"));
 	if (attrs.hasAttribute("NumerationRestart"))
 	{
 		NumerationRange numRange = (NumerationRange) attrs.valueAsInt("NumerationRestart");
diff --git a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
index a6e7b4b..e44c590 100644
--- a/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
+++ b/scribus/plugins/fileloader/scribus150format/scribus150format_save.cpp
@@ -864,6 +864,8 @@ void Scribus150Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle
 		docu.writeAttribute("NumerationPrefix", style.numPrefix());
 	if ( ! style.isInhNumSuffix())
 		docu.writeAttribute("NumerationSuffix", style.numSuffix());
+	if ( ! style.isInhNumAlignment())
+		docu.writeAttribute("NumerationAlignment", style.numAlignment());
 	if ( ! style.isInhNumStart())
 		docu.writeAttribute("NumerationStart", style.numStart());
 	if ( ! style.isInhNumRestart())
diff --git a/scribus/plugins/fileloader/scribus170format/scribus170format.cpp b/scribus/plugins/fileloader/scribus170format/scribus170format.cpp
index c186508..0aab66a 100644
--- a/scribus/plugins/fileloader/scribus170format/scribus170format.cpp
+++ b/scribus/plugins/fileloader/scribus170format/scribus170format.cpp
@@ -3187,6 +3187,10 @@ void Scribus170Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re
 	if (attrs.hasAttribute(NumerationSuffix))
 		newStyle.setNumSuffix(attrs.valueAsString(NumerationSuffix));
 
+	static const QString NumerationAlignment("NumerationAlignment");
+	if (attrs.hasAttribute(NumerationAlignment))
+		newStyle.setNumAlignment(attrs.valueAsInt(NumerationAlignment));
+
 	static const QString NumerationRestart("NumerationRestart");
 	if (attrs.hasAttribute(NumerationRestart))
 		newStyle.setNumRestart(attrs.valueAsInt(NumerationRestart));
@@ -5593,6 +5597,8 @@ PageItem* Scribus170Format::pasteItem(ScribusDoc *doc, const ScXmlStreamAttribut
 		pstyle.setNumPrefix(attrs.valueAsString("NumerationPrefix"));
 	if (attrs.hasAttribute("NumerationSuffix"))
 		pstyle.setNumSuffix(attrs.valueAsString("NumerationSuffix"));
+	if (attrs.hasAttribute("NumerationAlignment"))
+		pstyle.setNumAlignment(attrs.valueAsInt("NumerationAlignment"));
 	if (attrs.hasAttribute("NumerationRestart"))
 	{
 		NumerationRange numRange = (NumerationRange) attrs.valueAsInt("NumerationRestart");
diff --git a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
index 3b7a5ec..a1da959 100644
--- a/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
+++ b/scribus/plugins/fileloader/scribus170format/scribus170format_save.cpp
@@ -866,6 +866,8 @@ void Scribus170Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle
 		docu.writeAttribute("NumerationPrefix", style.numPrefix());
 	if (!style.isInhNumSuffix())
 		docu.writeAttribute("NumerationSuffix", style.numSuffix());
+	if (!style.isInhNumAlignment())
+		docu.writeAttribute("NumerationAlignment", style.numAlignment());
 	if (!style.isInhNumStart())
 		docu.writeAttribute("NumerationStart", style.numStart());
 	if (!style.isInhNumRestart())
diff --git a/scribus/plugins/fileloader/scribus171format/scribus171format.cpp b/scribus/plugins/fileloader/scribus171format/scribus171format.cpp
index 6c96651..e7ad277 100644
--- a/scribus/plugins/fileloader/scribus171format/scribus171format.cpp
+++ b/scribus/plugins/fileloader/scribus171format/scribus171format.cpp
@@ -3695,6 +3695,10 @@ void Scribus171Format::readParagraphStyle(ScribusDoc *doc, ScXmlStreamReader& re
 	if (attrs.hasAttribute(NumerationSuffix))
 		newStyle.setNumSuffix(attrs.valueAsString(NumerationSuffix));
 
+	static const QString NumerationAlignment("NumerationAlignment");
+	if (attrs.hasAttribute(NumerationAlignment))
+		newStyle.setNumAlignment(attrs.valueAsInt(NumerationAlignment));
+
 	static const QString NumerationRestart("NumerationRestart");
 	if (attrs.hasAttribute(NumerationRestart))
 		newStyle.setNumRestart(attrs.valueAsInt(NumerationRestart));
@@ -6717,6 +6721,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("NumerationAlignment"))
+		pstyle.setNumAlignment(attrs.valueAsInt("NumerationAlignment"));
 	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 ae959be..332870f 100644
--- a/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
+++ b/scribus/plugins/fileloader/scribus171format/scribus171format_save.cpp
@@ -868,6 +868,8 @@ void Scribus171Format::putPStyle(ScXmlStreamWriter & docu, const ParagraphStyle
 		docu.writeAttribute("NumerationPrefix", style.numPrefix());
 	if (!style.isInhNumSuffix())
 		docu.writeAttribute("NumerationSuffix", style.numSuffix());
+	if (!style.isInhNumAlignment())
+		docu.writeAttribute("NumerationAlignment", style.numAlignment());
 	if (!style.isInhNumStart())
 		docu.writeAttribute("NumerationStart", style.numStart());
 	if (!style.isInhNumRestart())
diff --git a/scribus/styles/paragraphstyle.attrdefs.cxx b/scribus/styles/paragraphstyle.attrdefs.cxx
index 238e71a..fb0ca66 100644
--- a/scribus/styles/paragraphstyle.attrdefs.cxx
+++ b/scribus/styles/paragraphstyle.attrdefs.cxx
@@ -49,6 +49,7 @@ ATTRDEF(QString, numName, NumName, "")
 ATTRDEF(int, numFormat, NumFormat, 0)
 ATTRDEF(QString, numPrefix, NumPrefix, "")
 ATTRDEF(QString, numSuffix, NumSuffix, ".")
+ATTRDEF(int, numAlignment, NumAlignment, ParagraphStyle::NumAlignmentSuffix)
 ATTRDEF(int, numLevel, NumLevel, 0)
 ATTRDEF(int, numStart, NumStart, 1)
 ATTRDEF(int, numRestart, NumRestart, 0)
diff --git a/scribus/styles/paragraphstyle.h b/scribus/styles/paragraphstyle.h
index 4e5bac2..f1d68d1 100644
--- a/scribus/styles/paragraphstyle.h
+++ b/scribus/styles/paragraphstyle.h
@@ -50,6 +50,14 @@ public:
 		RTL  = 1
 	};
 
+	enum NumAlignmentType
+	{
+		NumAlignmentLeft   = 0,
+		NumAlignmentCenter = 1,
+		NumAlignmentRight  = 2,
+		NumAlignmentSuffix = 3
+	};
+
 	enum OpticalMarginType
 	{
 		OM_None  = 0,
diff --git a/scribus/ui/propertywidget_pareffect.cpp b/scribus/ui/propertywidget_pareffect.cpp
index 0e6ef1e..03c8a63 100644
--- a/scribus/ui/propertywidget_pareffect.cpp
+++ b/scribus/ui/propertywidget_pareffect.cpp
@@ -5,7 +5,10 @@ a copyright and/or license notice that predates the release of Scribus 1.3.2
 for which a new license (GPL+exception) is in place.
 */
 
+#include <QPainter>
 #include <QSignalBlocker>
+#include <QStandardItem>
+#include <QStandardItemModel>
 
 #include "propertywidget_pareffect.h"
 
@@ -21,6 +24,29 @@ for which a new license (GPL+exception) is in place.
 #include "selection.h"
 #include "util.h"
 
+
+static QPixmap markerAlignmentFallbackPixmap(const QColor& color)
+{
+	QPixmap pixmap(16, 16);
+	pixmap.fill(Qt::transparent);
+
+	QPainter painter(&pixmap);
+	painter.setRenderHint(QPainter::Antialiasing, false);
+	painter.setPen(Qt::NoPen);
+	painter.setBrush(color);
+	painter.drawRect(5, 2, 1, 12);
+	painter.drawRect(3, 3, 5, 2);
+	painter.drawRect(1, 7, 7, 2);
+	painter.drawRect(3, 11, 5, 2);
+	painter.drawRect(10, 3, 5, 1);
+	painter.drawRect(10, 5, 4, 1);
+	painter.drawRect(10, 7, 5, 1);
+	painter.drawRect(10, 9, 4, 1);
+	painter.drawRect(10, 11, 5, 1);
+	painter.drawRect(10, 13, 4, 1);
+	return pixmap;
+}
+
 PropertyWidget_ParEffect::PropertyWidget_ParEffect(QWidget *parent) : QFrame(parent)
 {
 	setupUi(this);
@@ -31,6 +57,7 @@ PropertyWidget_ParEffect::PropertyWidget_ParEffect(QWidget *parent) : QFrame(par
 	if (m_doc)
 		peCharStyleCombo->updateStyleList();
 	fillBulletStrEditCombo();
+	fillNumAlignmentCombo();
 
 	numStart->setMinimum(1);
 	numStart->setMaximum(9999);
@@ -116,6 +143,13 @@ void PropertyWidget_ParEffect::iconSetChange()
 {
 	IconManager& iconManager = IconManager::instance();
 	bulletCharTableButton->setIcon(iconManager.loadIcon("tool-insert-table"));
+	QPixmap markerAlignmentPixmap = iconManager.loadPixmap("paragraph-list-marker-alignment");
+	if (markerAlignmentPixmap.isNull())
+		markerAlignmentPixmap = markerAlignmentFallbackPixmap(iconManager.baseColor());
+	numAlignmentLabel->setIconVisibility(true);
+	numAlignmentLabel->setIconDirection(FormWidget::Left);
+	numAlignmentLabel->setPixmapSize(QSize(16, 16));
+	numAlignmentLabel->setPixmap(markerAlignmentPixmap);
 	peCharStyleLabel->setPixmap(iconManager.loadPixmap("character-style"));
 	peOffsetLabel->setPixmap(iconManager.loadPixmap("paragraph-list-offset"));
 	peLabel->setPixmap(iconManager.loadPixmap("paragraph-effects-style"));
@@ -177,6 +211,7 @@ void PropertyWidget_ParEffect::setType(int id)
 	{
 		stackedWidget->setVisible(false);
 		peGroup->setVisible(false);
+		numAlignmentLabel->setVisible(false);
 		peCombo->setCurrentIndex(0);
 	}
 	else
@@ -184,6 +219,7 @@ void PropertyWidget_ParEffect::setType(int id)
 		stackedWidget->setCurrentIndex(id);
 		stackedWidget->setVisible(true);
 		peGroup->setVisible(true);
+		numAlignmentLabel->setVisible(id == 2);
 	}
 }
 
@@ -198,6 +234,39 @@ void PropertyWidget_ParEffect::fillBulletStrEditCombo()
 		bulletStrEdit->setEditText(QChar(0x2022));
 }
 
+void PropertyWidget_ParEffect::fillNumAlignmentCombo()
+{
+	const int oldAlignment = numAlignmentCombo->currentData().isValid()
+		? numAlignmentCombo->currentData().toInt()
+		: ParagraphStyle::NumAlignmentSuffix;
+	QSignalBlocker blocker(numAlignmentCombo);
+	numAlignmentCombo->clear();
+	numAlignmentCombo->addItem(tr("Left"), ParagraphStyle::NumAlignmentLeft);
+	numAlignmentCombo->addItem(tr("Center"), ParagraphStyle::NumAlignmentCenter);
+	numAlignmentCombo->addItem(tr("Right"), ParagraphStyle::NumAlignmentRight);
+	numAlignmentCombo->addItem(tr("Suffix"), ParagraphStyle::NumAlignmentSuffix);
+	const int index = numAlignmentCombo->findData(oldAlignment);
+	numAlignmentCombo->setCurrentIndex(index >= 0 ? index : numAlignmentCombo->findData(ParagraphStyle::NumAlignmentSuffix));
+	updateNumAlignmentSuffixState();
+}
+
+void PropertyWidget_ParEffect::updateNumAlignmentSuffixState()
+{
+	const int suffixIndex = numAlignmentCombo->findData(ParagraphStyle::NumAlignmentSuffix);
+	if (suffixIndex < 0)
+		return;
+
+	const bool hasSuffix = !numSuffix->text().isEmpty();
+	if (auto* model = qobject_cast<QStandardItemModel*>(numAlignmentCombo->model()))
+	{
+		if (QStandardItem* item = model->item(suffixIndex))
+			item->setEnabled(hasSuffix);
+	}
+
+	if (!hasSuffix && numAlignmentCombo->currentData().toInt() == ParagraphStyle::NumAlignmentSuffix)
+		numAlignmentCombo->setCurrentIndex(numAlignmentCombo->findData(ParagraphStyle::NumAlignmentRight));
+}
+
 void PropertyWidget_ParEffect::fillPECombo()
 {
 	QSignalBlocker sb(peCombo);
@@ -228,10 +297,11 @@ void PropertyWidget_ParEffect::updateStyle(const ParagraphStyle& newPStyle)
 	QSignalBlocker blocker6(numFormatCombo);
 	QSignalBlocker blocker7(numPrefix);
 	QSignalBlocker blocker8(numSuffix);
-	QSignalBlocker blocker9(numStart);
-	QSignalBlocker blockerA(peOffset);
-	QSignalBlocker blockerB(peIndent);
-	QSignalBlocker blockerC(peCharStyleCombo);
+	QSignalBlocker blocker9(numAlignmentCombo);
+	QSignalBlocker blockerA(numStart);
+	QSignalBlocker blockerB(peOffset);
+	QSignalBlocker blockerC(peIndent);
+	QSignalBlocker blockerD(peCharStyleCombo);
 
 	if (newPStyle.hasDropCap())
 	{
@@ -265,6 +335,11 @@ void PropertyWidget_ParEffect::updateStyle(const ParagraphStyle& newPStyle)
 	numLevelSpin->setValue(newPStyle.numLevel() + 1);
 	numPrefix->setText(newPStyle.numPrefix());
 	numSuffix->setText(newPStyle.numSuffix());
+	int alignmentIndex = numAlignmentCombo->findData(newPStyle.numAlignment());
+	if (alignmentIndex < 0)
+		alignmentIndex = numAlignmentCombo->findData(ParagraphStyle::NumAlignmentSuffix);
+	numAlignmentCombo->setCurrentIndex(alignmentIndex);
+	updateNumAlignmentSuffixState();
 	numStart->setValue(newPStyle.numStart());
 
 	numFormatCombo->setCurrentFormat((NumFormat) newPStyle.numFormat());
@@ -286,6 +361,7 @@ void PropertyWidget_ParEffect::connectSignals()
 	connect(numFormatCombo, SIGNAL(activated(int)), this, SLOT(handleNumFormat(int)), Qt::UniqueConnection);
 	connect(numPrefix, SIGNAL(textChanged(QString)), this, SLOT(handleNumPrefix(QString)), Qt::UniqueConnection);
 	connect(numSuffix, SIGNAL(textChanged(QString)), this, SLOT(handleNumSuffix(QString)), Qt::UniqueConnection);
+	connect(numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(handleNumAlignment(int)), 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);
@@ -302,6 +378,7 @@ void PropertyWidget_ParEffect::disconnectSignals()
 	disconnect(numFormatCombo, SIGNAL(activated(int)), this, SLOT(handleNumFormat(int)));
 	disconnect(numPrefix, SIGNAL(textChanged(QString)), this, SLOT(handleNumPrefix(QString)));
 	disconnect(numSuffix, SIGNAL(textChanged(QString)), this, SLOT(handleNumSuffix(QString)));
+	disconnect(numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(handleNumAlignment(int)));
 	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)));
@@ -391,6 +468,7 @@ void PropertyWidget_ParEffect::handleParEffectUse()
 		newStyle.setNumStart(numStart->value());
 		newStyle.setNumPrefix(numPrefix->text());
 		newStyle.setNumSuffix(numSuffix->text());
+		newStyle.setNumAlignment(numAlignmentCombo->currentData().toInt());
 		if (newStyle.numName() == "<local block>")
 		{
 			newStyle.setNumOther(true);
@@ -460,6 +538,8 @@ void PropertyWidget_ParEffect::handleNumName(const QString& numName)
 	}
 	newStyle.setNumPrefix(numPrefix->text());
 	newStyle.setNumSuffix(numSuffix->text());
+	updateNumAlignmentSuffixState();
+	newStyle.setNumAlignment(numAlignmentCombo->currentData().toInt());
 	newStyle.setNumName(numName);
 	newStyle.setNumFormat(numFormatCombo->currentFormat());
 	handleChanges(m_item, newStyle);
@@ -516,6 +596,21 @@ void PropertyWidget_ParEffect::handleNumSuffix(const QString& suffix)
 		return;
 	ParagraphStyle newStyle;
 	newStyle.setNumSuffix(suffix);
+	if (suffix.isEmpty() && numAlignmentCombo->currentData().toInt() == ParagraphStyle::NumAlignmentSuffix)
+	{
+		numAlignmentCombo->setCurrentIndex(numAlignmentCombo->findData(ParagraphStyle::NumAlignmentRight));
+		newStyle.setNumAlignment(ParagraphStyle::NumAlignmentRight);
+	}
+	updateNumAlignmentSuffixState();
+	handleChanges(m_item, newStyle);
+}
+
+void PropertyWidget_ParEffect::handleNumAlignment(int)
+{
+	if (!m_doc || !m_item)
+		return;
+	ParagraphStyle newStyle;
+	newStyle.setNumAlignment(numAlignmentCombo->currentData().toInt());
 	handleChanges(m_item, newStyle);
 }
 
@@ -586,8 +681,9 @@ void PropertyWidget_ParEffect::handleChanges(PageItem *item, ParagraphStyle &new
 
 void PropertyWidget_ParEffect::languageChange()
 {
-	fillPECombo();
 	retranslateUi(this);
+	fillPECombo();
+	fillNumAlignmentCombo();
 }
 
 void PropertyWidget_ParEffect::openEnhanced()
diff --git a/scribus/ui/propertywidget_pareffect.h b/scribus/ui/propertywidget_pareffect.h
index 62729f7..7a89df1 100644
--- a/scribus/ui/propertywidget_pareffect.h
+++ b/scribus/ui/propertywidget_pareffect.h
@@ -64,6 +64,7 @@ public slots:
 	void handleNumLevel(int);
 	void handleNumPrefix(const QString&);
 	void handleNumSuffix(const QString&);
+	void handleNumAlignment(int);
 	void handleNumStart(int);
 	void handlePEOffset(double);
 	void handlePEIndent(bool);
@@ -78,6 +79,8 @@ private:
 	void closeEnhanced(bool show = false);
 	void setType(int id);
 	void fillBulletStrEditCombo();
+	void fillNumAlignmentCombo();
+	void updateNumAlignmentSuffixState();
 	void fillPECombo();
 
 signals:
diff --git a/scribus/ui/propertywidget_pareffectbase.ui b/scribus/ui/propertywidget_pareffectbase.ui
index 1a2670a..0e45c20 100644
--- a/scribus/ui/propertywidget_pareffectbase.ui
+++ b/scribus/ui/propertywidget_pareffectbase.ui
@@ -245,6 +245,9 @@
        </item>
        <item row="2" column="1">
         <widget class="QSpinBox" name="numLevelSpin">
+         <property name="toolTip">
+          <string>Hierarchy level within the numbering set</string>
+         </property>
          <property name="minimum">
           <number>1</number>
          </property>
@@ -340,6 +343,9 @@
        </item>
        <item row="2" column="4">
         <widget class="QSpinBox" name="numStart">
+         <property name="toolTip">
+          <string>First number used when the numbering sequence restarts</string>
+         </property>
          <property name="minimum">
           <number>1</number>
          </property>
@@ -349,13 +355,24 @@
         </widget>
        </item>
        <item row="3" column="1">
-        <widget class="QLineEdit" name="numPrefix"/>
+        <widget class="QLineEdit" name="numPrefix">
+         <property name="toolTip">
+          <string>Text placed before the list number</string>
+         </property>
+        </widget>
        </item>
        <item row="3" column="4">
-        <widget class="QLineEdit" name="numSuffix"/>
+        <widget class="QLineEdit" name="numSuffix">
+         <property name="toolTip">
+          <string>Text placed after the list number; required for Suffix marker alignment</string>
+         </property>
+        </widget>
        </item>
        <item row="1" column="1" colspan="4">
         <widget class="NumFormatCombo" name="numFormatCombo">
+         <property name="toolTip">
+          <string>Number format used for this list level</string>
+         </property>
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
            <horstretch>0</horstretch>
@@ -398,6 +415,43 @@
         <property name="spacing">
          <number>20</number>
         </property>
+        <item>
+         <widget class="FormWidget" name="numAlignmentLabel">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="label" stdset="0">
+           <string>Marker Alignment</string>
+          </property>
+          <property name="useSmallFont">
+           <bool>true</bool>
+          </property>
+          <layout class="QHBoxLayout" name="horizontalLayout_numAlignment">
+           <property name="leftMargin">
+            <number>0</number>
+           </property>
+           <property name="topMargin">
+            <number>0</number>
+           </property>
+           <property name="rightMargin">
+            <number>0</number>
+           </property>
+           <property name="bottomMargin">
+            <number>0</number>
+           </property>
+           <item>
+            <widget class="QComboBox" name="numAlignmentCombo">
+             <property name="toolTip">
+              <string>Choose which part of the numbered-list marker is anchored at the paragraph indent; Suffix aligns the beginning of the suffix</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
         <item>
          <widget class="FormWidget" name="peOffsetLabel">
           <property name="sizePolicy">
@@ -407,7 +461,7 @@
            </sizepolicy>
           </property>
           <property name="label" stdset="0">
-           <string/>
+           <string>Marker-Text Gap</string>
           </property>
           <layout class="QHBoxLayout" name="horizontalLayout_2">
            <property name="leftMargin">
@@ -425,7 +479,7 @@
            <item>
             <widget class="ScrSpinBox" name="peOffset">
              <property name="toolTip">
-              <string>Paragraph Effects characters offset</string>
+              <string>Distance between the paragraph-effect marker and the paragraph text</string>
              </property>
              <property name="minimum">
               <double>-3000.000000000000000</double>
@@ -440,8 +494,11 @@
         </item>
         <item>
          <widget class="QCheckBox" name="peIndent">
+          <property name="toolTip">
+           <string>Automatically create a hanging indent so the paragraph text starts after the marker</string>
+          </property>
           <property name="text">
-           <string>Auto-Indent</string>
+           <string>Auto Hanging Indent</string>
           </property>
          </widget>
         </item>
@@ -474,7 +531,7 @@
            </sizepolicy>
           </property>
           <property name="label" stdset="0">
-           <string/>
+           <string>Character Style</string>
           </property>
           <layout class="QHBoxLayout" name="horizontalLayout_3">
            <property name="leftMargin">
@@ -492,7 +549,7 @@
            <item>
             <widget class="CharStyleComboBox" name="peCharStyleCombo">
              <property name="toolTip">
-              <string>Choose Character Style or leave blank to use default Paragraph Style</string>
+              <string>Character style applied to the drop cap, bullet, or numbered-list marker; leave blank to use the paragraph style</string>
              </property>
             </widget>
            </item>
@@ -556,6 +613,7 @@
   <tabstop>numStart</tabstop>
   <tabstop>numPrefix</tabstop>
   <tabstop>numSuffix</tabstop>
+  <tabstop>numAlignmentCombo</tabstop>
   <tabstop>peOffset</tabstop>
   <tabstop>peIndent</tabstop>
   <tabstop>peCharStyleCombo</tabstop>
diff --git a/scribus/ui/smpstylewidget.cpp b/scribus/ui/smpstylewidget.cpp
index 12fde44..5fb96f6 100644
--- a/scribus/ui/smpstylewidget.cpp
+++ b/scribus/ui/smpstylewidget.cpp
@@ -6,6 +6,10 @@ for which a new license (GPL+exception) is in place.
 */
 
 #include <QEvent>
+#include <QPainter>
+#include <QSignalBlocker>
+#include <QStandardItem>
+#include <QStandardItemModel>
 
 
 #include "iconmanager.h"
@@ -25,6 +29,30 @@ static bool isEqual(double a, double b)
 	return al == bl;
 }
 
+static QPixmap markerAlignmentFallbackPixmap(const QColor& color)
+{
+	QPixmap pixmap(16, 16);
+	pixmap.fill(Qt::transparent);
+
+	QPainter painter(&pixmap);
+	painter.setRenderHint(QPainter::Antialiasing, false);
+	painter.setPen(Qt::NoPen);
+	painter.setBrush(color);
+
+	painter.drawRect(5, 2, 1, 12);
+	painter.drawRect(3, 3, 5, 2);
+	painter.drawRect(1, 7, 7, 2);
+	painter.drawRect(3, 11, 5, 2);
+	painter.drawRect(10, 3, 5, 1);
+	painter.drawRect(10, 5, 4, 1);
+	painter.drawRect(10, 7, 5, 1);
+	painter.drawRect(10, 9, 4, 1);
+	painter.drawRect(10, 11, 5, 1);
+	painter.drawRect(10, 13, 4, 1);
+
+	return pixmap;
+}
+
 
 SMPStyleWidget::SMPStyleWidget(ScribusDoc* doc, StyleSet<CharStyle> *cstyles) :
 	m_Doc(doc),
@@ -70,6 +98,7 @@ SMPStyleWidget::SMPStyleWidget(ScribusDoc* doc, StyleSet<CharStyle> *cstyles) :
 	numSuffix->setMaxLength(5);
 	numSuffix->setMaximumWidth(QFontMetrics(numSuffix->font()).averageCharWidth() * 7);
 	fillNumRestartCombo();
+	fillNumAlignmentCombo();
 	dropCapLines->setMinimum(2);
 	dropCapLines->setMaximum(99);
 
@@ -118,6 +147,13 @@ void SMPStyleWidget::iconSetChange()
 	consecutiveHyphenLabel->setPixmap(iconManager.loadPixmap("hyphen-consecutive"));
 
 	bulletCharTableButton->setIcon(iconManager.loadIcon("character-table"));
+	QPixmap markerAlignmentPixmap = iconManager.loadPixmap("paragraph-list-marker-alignment");
+	if (markerAlignmentPixmap.isNull())
+		markerAlignmentPixmap = markerAlignmentFallbackPixmap(iconManager.baseColor());
+	numAlignmentLabel->setIconVisibility(true);
+	numAlignmentLabel->setIconDirection(FormWidget::Left);
+	numAlignmentLabel->setPixmapSize(QSize(16, 16));
+	numAlignmentLabel->setPixmap(markerAlignmentPixmap);
 	distFromTextLabel->setPixmap(iconManager.loadPixmap("paragraph-list-offset"));
 	parEffectCharStyleComboLabel->setPixmap(iconManager.loadPixmap("character-style"));
 	peLabel->setPixmap(iconManager.loadPixmap("paragraph-effects-style"));
@@ -155,6 +191,7 @@ void SMPStyleWidget::languageChange()
 	numRestartCombo->setCurrentIndex(oldNumRestartIndex);
 	numRestartCombo->blockSignals(numRestartComboBlocked);
 
+	fillNumAlignmentCombo();
 	fillPECombo();
 
 	backgroundColor->colorButton->setPersistentToolTip( tr("Background color of selected text"));
@@ -331,6 +368,9 @@ void SMPStyleWidget::show(ParagraphStyle *pstyle, QList<ParagraphStyle> &pstyles
 		setWidgetBoldFont(numPrefixLabel, !pstyle->isInhNumPrefix());
 		numSuffix->setText(pstyle->numSuffix());
 		setWidgetBoldFont(numSuffixLabel, !pstyle->isInhNumSuffix());
+		numAlignmentCombo->setCurrentItemByData(pstyle->numAlignment(), pstyle->isInhNumAlignment());
+		numAlignmentCombo->setParentItem(numAlignmentCombo->getItemIndexForData(parent->numAlignment()));
+		updateNumAlignmentSuffixState();
 		numStartSpin->setValue(pstyle->numStart(), pstyle->isInhNumStart());
 		numStartSpin->setParentValue(parent->numStart());
 		int numRestartIndex = numRestartCombo->findData(pstyle->numRestart());
@@ -379,6 +419,8 @@ void SMPStyleWidget::show(ParagraphStyle *pstyle, QList<ParagraphStyle> &pstyles
 		setWidgetBoldFont(numPrefixLabel, false);
 		numSuffix->setText(pstyle->numSuffix());
 		setWidgetBoldFont(numSuffixLabel, false);
+		numAlignmentCombo->setCurrentItemByData(pstyle->numAlignment());
+		updateNumAlignmentSuffixState();
 		numStartSpin->setValue(pstyle->numStart());
 		int numRestartIndex = numRestartCombo->findData(pstyle->numRestart());
 		numRestartCombo->setCurrentItem((numRestartIndex >= 0) ? numRestartIndex : 0);
@@ -988,6 +1030,38 @@ void SMPStyleWidget::fillNumRestartCombo()
 	numRestartCombo->addItem(tr("Story") , static_cast<int>(NSRstory));
 }
 
+void SMPStyleWidget::fillNumAlignmentCombo()
+{
+	const int oldAlignment = numAlignmentCombo->currentData().isValid()
+		? numAlignmentCombo->currentData().toInt()
+		: ParagraphStyle::NumAlignmentSuffix;
+	QSignalBlocker blocker(numAlignmentCombo);
+	numAlignmentCombo->clear();
+	numAlignmentCombo->addItem(tr("Left"), ParagraphStyle::NumAlignmentLeft);
+	numAlignmentCombo->addItem(tr("Center"), ParagraphStyle::NumAlignmentCenter);
+	numAlignmentCombo->addItem(tr("Right"), ParagraphStyle::NumAlignmentRight);
+	numAlignmentCombo->addItem(tr("Suffix"), ParagraphStyle::NumAlignmentSuffix);
+	numAlignmentCombo->setCurrentItemByData(oldAlignment);
+	updateNumAlignmentSuffixState();
+}
+
+void SMPStyleWidget::updateNumAlignmentSuffixState()
+{
+	const int suffixIndex = numAlignmentCombo->findData(ParagraphStyle::NumAlignmentSuffix);
+	if (suffixIndex < 0)
+		return;
+
+	const bool hasSuffix = !numSuffix->text().isEmpty();
+	if (auto* model = qobject_cast<QStandardItemModel*>(numAlignmentCombo->model()))
+	{
+		if (QStandardItem* item = model->item(suffixIndex))
+			item->setEnabled(hasSuffix);
+	}
+
+	if (!hasSuffix && numAlignmentCombo->currentData().toInt() == ParagraphStyle::NumAlignmentSuffix)
+		numAlignmentCombo->setCurrentItemByData(ParagraphStyle::NumAlignmentRight);
+}
+
 void SMPStyleWidget::fillPECombo()
 {
 	QSignalBlocker sb(peCombo);
@@ -1012,6 +1086,7 @@ void SMPStyleWidget::setParagraphEffect(int index)
 		stackedWidget->setCurrentIndex(id - 1);
 		stackedWidget->setVisible(true);
 		peGroup->setVisible(true);
+		numAlignmentLabel->setVisible(id == 3);
 
 		if (id == 2) // Bullet List
 		{
@@ -1028,6 +1103,7 @@ void SMPStyleWidget::setParagraphEffect(int index)
 	{
 		stackedWidget->setVisible(false);
 		peGroup->setVisible(false);
+		numAlignmentLabel->setVisible(false);
 		peCombo->setCurrentIndex(0);
 	}
 }
@@ -1119,6 +1195,18 @@ void SMPStyleWidget::showNumeration(const QList<ParagraphStyle *> &pstyles, cons
 		suffix = pstyles[i]->numSuffix();
 	}
 	numSuffix->setText(suffix);
+
+	int alignment = pstyles[0]->numAlignment();
+	for (int i = 1; i < pstyles.count(); ++i)
+	{
+		if (alignment != pstyles[i]->numAlignment())
+		{
+			alignment = ParagraphStyle::NumAlignmentRight;
+			break;
+		}
+	}
+	numAlignmentCombo->setCurrentItemByData(alignment);
+	updateNumAlignmentSuffixState();
 }
 
 void SMPStyleWidget::handleParEffectUse(int index)
diff --git a/scribus/ui/smpstylewidget.h b/scribus/ui/smpstylewidget.h
index c985d70..ec3e025 100644
--- a/scribus/ui/smpstylewidget.h
+++ b/scribus/ui/smpstylewidget.h
@@ -51,6 +51,8 @@ private:
 	void fillBulletStrEditCombo();
 	void fillNumerationsCombo();
 	void fillNumRestartCombo();
+	void fillNumAlignmentCombo();
+	void updateNumAlignmentSuffixState();
 	void fillPECombo();
 	void setParagraphEffect(int);
 	void showColors(const QList<ParagraphStyle*> &cstyles);
diff --git a/scribus/ui/smpstylewidget.ui b/scribus/ui/smpstylewidget.ui
index 8a6d90e..beeb751 100644
--- a/scribus/ui/smpstylewidget.ui
+++ b/scribus/ui/smpstylewidget.ui
@@ -1291,8 +1291,11 @@ A value of 0 means unlimited hyphenations.</string>
                </property>
               </widget>
              </item>
-             <item row="4" column="6">
+             <item row="5" column="1" colspan="4">
               <widget class="SMCheckBox" name="numRestartOtherBox">
+               <property name="toolTip">
+                <string>Restart when the preceding paragraph is unnumbered or uses a different numbering set</string>
+               </property>
                <property name="text">
                 <string>Restart after other format</string>
                </property>
@@ -1301,7 +1304,7 @@ A value of 0 means unlimited hyphenations.</string>
              <item row="3" column="1">
               <widget class="QLineEdit" name="numPrefix">
                <property name="toolTip">
-                <string>List number prefix</string>
+                <string>Text placed before the list number</string>
                </property>
               </widget>
              </item>
@@ -1325,8 +1328,11 @@ A value of 0 means unlimited hyphenations.</string>
                </property>
               </widget>
              </item>
-             <item row="5" column="6">
+             <item row="6" column="1" colspan="4">
               <widget class="SMCheckBox" name="numRestartHigherBox">
+               <property name="toolTip">
+                <string>Restart this level when it follows an item at a higher list level</string>
+               </property>
                <property name="text">
                 <string>Restart after higher level</string>
                </property>
@@ -1351,7 +1357,7 @@ A value of 0 means unlimited hyphenations.</string>
              <item row="3" column="4">
               <widget class="QLineEdit" name="numSuffix">
                <property name="toolTip">
-                <string>List number suffix</string>
+                <string>Text placed after the list number; required for Suffix marker alignment</string>
                </property>
               </widget>
              </item>
@@ -1384,12 +1390,16 @@ A value of 0 means unlimited hyphenations.</string>
              <item row="2" column="1">
               <widget class="SMSpinBox" name="numLevelSpin">
                <property name="toolTip">
-                <string>Hierarchy level of list numbering</string>
+                <string>Hierarchy level within the numbering set</string>
                </property>
               </widget>
              </item>
              <item row="4" column="1" colspan="4">
-              <widget class="SMScComboBox" name="numRestartCombo"/>
+              <widget class="SMScComboBox" name="numRestartCombo">
+               <property name="toolTip">
+                <string>Choose whether this numbering sequence continues through the document or restarts for each story</string>
+               </property>
+              </widget>
              </item>
              <item row="4" column="7">
               <spacer name="horizontalSpacer_9">
@@ -1413,14 +1423,14 @@ A value of 0 means unlimited hyphenations.</string>
                 </sizepolicy>
                </property>
                <property name="toolTip">
-                <string>List numbering style</string>
+                <string>Number format used for this list level</string>
                </property>
               </widget>
              </item>
              <item row="2" column="4">
               <widget class="SMSpinBox" name="numStartSpin">
                <property name="toolTip">
-                <string>Starting number of the list count</string>
+                <string>First number used when the numbering sequence restarts</string>
                </property>
                <property name="maximum">
                 <number>99999</number>
@@ -1505,6 +1515,43 @@ A value of 0 means unlimited hyphenations.</string>
             <property name="spacing">
              <number>16</number>
             </property>
+            <item>
+             <widget class="FormWidget" name="numAlignmentLabel">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+                <horstretch>0</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="label" stdset="0">
+               <string>Marker Alignment</string>
+              </property>
+              <property name="useSmallFont">
+               <bool>true</bool>
+              </property>
+              <layout class="QHBoxLayout" name="horizontalLayout_numAlignment">
+               <property name="leftMargin">
+                <number>0</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <widget class="SMScComboBox" name="numAlignmentCombo">
+                 <property name="toolTip">
+                  <string>Choose which part of the numbered-list marker is anchored at the paragraph indent; Suffix aligns the beginning of the suffix</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
             <item>
              <widget class="FormWidget" name="distFromTextLabel">
               <property name="sizePolicy">
@@ -1514,7 +1561,7 @@ A value of 0 means unlimited hyphenations.</string>
                </sizepolicy>
               </property>
               <property name="label" stdset="0">
-               <string>Distance</string>
+               <string>Marker-Text Gap</string>
               </property>
               <layout class="QHBoxLayout" name="horizontalLayout_22">
                <property name="leftMargin">
@@ -1532,7 +1579,7 @@ A value of 0 means unlimited hyphenations.</string>
                <item>
                 <widget class="SMScrSpinBox" name="parEffectOffset">
                  <property name="toolTip">
-                  <string>Paragraph Effects characters offset</string>
+                  <string>Distance between the paragraph-effect marker and the paragraph text</string>
                  </property>
                  <property name="minimum">
                   <double>-3000.000000000000000</double>
@@ -1575,10 +1622,10 @@ A value of 0 means unlimited hyphenations.</string>
                <item>
                 <widget class="SMCheckBox" name="parEffectIndentBox">
                  <property name="toolTip">
-                  <string>Hang Paragraph Effect before paragraph indent</string>
+                  <string>Automatically create a hanging indent so the paragraph text starts after the marker</string>
                  </property>
                  <property name="text">
-                  <string>Auto-Indent</string>
+                  <string>Auto Hanging Indent</string>
                  </property>
                 </widget>
                </item>
@@ -1614,7 +1661,7 @@ A value of 0 means unlimited hyphenations.</string>
                </sizepolicy>
               </property>
               <property name="label" stdset="0">
-               <string>Style</string>
+               <string>Character Style</string>
               </property>
               <layout class="QHBoxLayout" name="horizontalLayout_23">
                <property name="leftMargin">
@@ -1632,7 +1679,7 @@ A value of 0 means unlimited hyphenations.</string>
                <item>
                 <widget class="SMScComboBox" name="parEffectCharStyleCombo">
                  <property name="toolTip">
-                  <string>Choose Character Style or leave blank to use default Paragraph Style</string>
+                  <string>Character style applied to the drop cap, bullet, or numbered-list marker; leave blank to use the paragraph style</string>
                  </property>
                 </widget>
                </item>
@@ -1791,6 +1838,7 @@ A value of 0 means unlimited hyphenations.</string>
   <tabstop>peCombo</tabstop>
   <tabstop>parentParEffectsButton</tabstop>
   <tabstop>dropCapLines</tabstop>
+  <tabstop>numAlignmentCombo</tabstop>
   <tabstop>parEffectOffset</tabstop>
   <tabstop>parEffectIndentBox</tabstop>
   <tabstop>parEffectCharStyleCombo</tabstop>
diff --git a/scribus/ui/smtextstyles.cpp b/scribus/ui/smtextstyles.cpp
index b021322..d09e513 100644
--- a/scribus/ui/smtextstyles.cpp
+++ b/scribus/ui/smtextstyles.cpp
@@ -523,6 +523,7 @@ void SMParagraphStyle::setupConnections()
 	connect(m_pwidget->numRestartHigherBox, SIGNAL(toggled(bool)), this, SLOT(slotNumHigher(bool)));
 	connect(m_pwidget->numPrefix, SIGNAL(textChanged(QString)), this, SLOT(slotNumPrefix(QString)));
 	connect(m_pwidget->numSuffix, SIGNAL(textChanged(QString)), this, SLOT(slotNumSuffix(QString)));
+	connect(m_pwidget->numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(slotNumAlignment(int)));
 	connect(m_pwidget->numNewLineEdit, SIGNAL(editingFinished()), this, SLOT(slotNumNew()));
 	connect(m_pwidget->numNewLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotSelectionDirty()));
 
@@ -618,6 +619,7 @@ void SMParagraphStyle::removeConnections()
 	disconnect(m_pwidget->numRestartHigherBox, SIGNAL(toggled(bool)), this, SLOT(slotNumHigher(bool)));
 	disconnect(m_pwidget->numPrefix, SIGNAL(textChanged(QString)), this, SLOT(slotNumPrefix(QString)));
 	disconnect(m_pwidget->numSuffix, SIGNAL(textChanged(QString)), this, SLOT(slotNumSuffix(QString)));
+	disconnect(m_pwidget->numAlignmentCombo, SIGNAL(activated(int)), this, SLOT(slotNumAlignment(int)));
 	disconnect(m_pwidget->numNewLineEdit, SIGNAL(editingFinished()), this, SLOT(slotNumNew()));
 	disconnect(m_pwidget->numNewLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotSelectionDirty()));
 	
@@ -910,6 +912,7 @@ void SMParagraphStyle::slotPargraphEffects(int index)
 			m_selection[i]->setHasDropCap(false);
 			m_selection[i]->setHasBullet(false);
 			m_selection[i]->setHasNum(true);
+			m_selection[i]->setNumAlignment(m_pwidget->numAlignmentCombo->currentData().toInt());
 		}
 		else // No effect
 		{
@@ -1118,6 +1121,30 @@ void SMParagraphStyle::slotNumSuffix(const QString &str)
 	for (int i = 0; i < m_selection.count(); ++i)
 		m_selection[i]->setNumSuffix(str);
 
+	if (str.isEmpty() && m_pwidget->numAlignmentCombo->currentData().toInt() == ParagraphStyle::NumAlignmentSuffix)
+	{
+		m_pwidget->numAlignmentCombo->setCurrentItemByData(ParagraphStyle::NumAlignmentRight);
+		for (int i = 0; i < m_selection.count(); ++i)
+			m_selection[i]->setNumAlignment(ParagraphStyle::NumAlignmentRight);
+	}
+	m_pwidget->updateNumAlignmentSuffixState();
+	slotSelectionDirty();
+}
+
+void SMParagraphStyle::slotNumAlignment(int)
+{
+	if (m_pwidget->numAlignmentCombo->useParentValue())
+	{
+		for (int i = 0; i < m_selection.count(); ++i)
+			m_selection[i]->resetNumAlignment();
+	}
+	else
+	{
+		const int alignment = m_pwidget->numAlignmentCombo->currentData().toInt();
+		for (int i = 0; i < m_selection.count(); ++i)
+			m_selection[i]->setNumAlignment(alignment);
+	}
+
 	slotSelectionDirty();
 }
 
diff --git a/scribus/ui/smtextstyles.h b/scribus/ui/smtextstyles.h
index 3dd0235..6f8af4e 100644
--- a/scribus/ui/smtextstyles.h
+++ b/scribus/ui/smtextstyles.h
@@ -87,6 +87,7 @@ private slots:
 	void slotNumLevel(int level);
 	void slotNumPrefix(const QString &str);
 	void slotNumSuffix(const QString &str);
+	void slotNumAlignment(int);
 	void slotNumStart(int start);
 	void slotNumRestart(int restart);
 	void slotNumOther(bool isOn);

qirat

2026-07-11 06:27

reporter   ~0054014

2_list-marker-alignment_annot.jpg (155,749 bytes)   
2_list-marker-alignment_annot.jpg (155,749 bytes)   

Issue History

Date Modified Username Field Change
2026-02-24 13:34 qirat New Issue
2026-02-24 13:34 qirat File Added: scribus_align_suffix.jpg
2026-07-01 01:51 qirat Note Added: 0053912
2026-07-09 04:45 qirat Note Added: 0053998
2026-07-09 04:45 qirat File Added: list-marker-alignment-v1.1-stylemanager-popup-fix.patch
2026-07-09 04:46 qirat Note Added: 0053999
2026-07-09 04:46 qirat File Added: context_list-marker-alignment.md
2026-07-09 04:46 qirat File Added: list-marker-alignment_100items_test.sla
2026-07-11 06:27 qirat Note Added: 0054013
2026-07-11 06:27 qirat File Added: 2_list-marker-alignment-v1.5_layout-labels-tooltips.patch
2026-07-11 06:27 qirat Note Added: 0054014
2026-07-11 06:27 qirat File Added: 2_list-marker-alignment_annot.jpg