diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml
index 6c122ebc54..c139b35660 100644
--- a/.github/workflows/link-check.yml
+++ b/.github/workflows/link-check.yml
@@ -11,7 +11,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Link check with lychee (non-blocking)
- uses: lycheeverse/lychee-action@v2.0.2
+ uses: lycheeverse/lychee-action@v2.8.0
with:
# Limit concurrency and accept 429 (rate-limited) as non-fatal
args: >-
diff --git a/Build/SilVersions.props b/Build/SilVersions.props
index de5e0cd9da..e99438cac3 100644
--- a/Build/SilVersions.props
+++ b/Build/SilVersions.props
@@ -12,7 +12,7 @@
=============================================================
-->
- 11.0.0-beta0156
+ 11.0.0-beta0158
17.0.0
6.0.0-beta0063
3.7.13
diff --git a/Build/Src/FwBuildTasks/FwBuildTasks.csproj b/Build/Src/FwBuildTasks/FwBuildTasks.csproj
index ca96a9f190..8a6ccaba5c 100644
--- a/Build/Src/FwBuildTasks/FwBuildTasks.csproj
+++ b/Build/Src/FwBuildTasks/FwBuildTasks.csproj
@@ -21,12 +21,12 @@
false
-
-
+
+
-
-
-
+
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 7c13c8d7af..c9d4649642 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -25,20 +25,20 @@
-
+
-
+
-
+
-
+
-
-
+
+
-
+
diff --git a/Obj/FwBuildTasks/FwBuildTasks.csproj.nuget.g.props b/Obj/FwBuildTasks/FwBuildTasks.csproj.nuget.g.props
new file mode 100644
index 0000000000..f23b00e175
--- /dev/null
+++ b/Obj/FwBuildTasks/FwBuildTasks.csproj.nuget.g.props
@@ -0,0 +1,26 @@
+
+
+
+ True
+ NuGet
+ $(MSBuildThisFileDirectory)project.assets.json
+ /home/dependabot/dependabot-updater/repo/packages
+ /home/dependabot/dependabot-updater/repo/packages
+ PackageReference
+ 7.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /home/dependabot/dependabot-updater/repo/packages/sil.buildtasks/3.2.0
+
+
\ No newline at end of file
diff --git a/Obj/FwBuildTasks/FwBuildTasks.csproj.nuget.g.targets b/Obj/FwBuildTasks/FwBuildTasks.csproj.nuget.g.targets
new file mode 100644
index 0000000000..6f260c202e
--- /dev/null
+++ b/Obj/FwBuildTasks/FwBuildTasks.csproj.nuget.g.targets
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Src/views/Test/TestVwTextBoxes.h b/Src/views/Test/TestVwTextBoxes.h
index bc278f8ef6..a53e100ca9 100644
--- a/Src/views/Test/TestVwTextBoxes.h
+++ b/Src/views/Test/TestVwTextBoxes.h
@@ -61,6 +61,45 @@ namespace TestViews
IPicturePtr m_qPicture;
};
+ // View constructor that displays a single string inside a concordance paragraph.
+ // The keyword region (ichMin..ichLim) is aligned at dmpAlign.
+ // If textAlign is set to ktalRight, the paragraph gets right alignment so that
+ // DoSpecialAlignment uses the right edge of the keyword for positioning.
+ class ConcParaVc : public DummyBaseVc
+ {
+ public:
+ int m_ichMinItem;
+ int m_ichLimItem;
+ int m_dmpAlign;
+ int m_textAlign; // ktalLeft or ktalRight
+
+ ConcParaVc(int ichMin, int ichLim, int dmpAlign, int textAlign)
+ : m_ichMinItem(ichMin), m_ichLimItem(ichLim),
+ m_dmpAlign(dmpAlign), m_textAlign(textAlign)
+ {
+ }
+
+ STDMETHOD(Display)(IVwEnv * pvwenv, HVO hvo, int frag)
+ {
+ pvwenv->put_IntProperty(ktptAlign, ktpvEnum, m_textAlign);
+ pvwenv->OpenConcPara(m_ichMinItem, m_ichLimItem,
+ (VwConcParaOpts)kcpoDefault, m_dmpAlign);
+ pvwenv->AddStringProp(kflidStTxtPara_Contents, NULL);
+ pvwenv->CloseParagraph();
+ return S_OK;
+ }
+ STDMETHOD(EstimateHeight)(HVO hvo, int frag, int dxAvailWidth, int * pdyHeight)
+ {
+ *pdyHeight = 20;
+ return S_OK;
+ }
+ STDMETHOD(LoadDataFor)(IVwEnv * pvwenv, HVO * prghvo, int chvo, HVO hvoParent,
+ int tag, int frag, int ihvoMin)
+ {
+ return S_OK;
+ }
+ };
+
class TestVwParagraphBox : public unitpp::suite
{
// Maximum number of previous strings to add before testing strings
@@ -1207,6 +1246,68 @@ namespace TestViews
}
}
+ // Helper: lay out a single concordance paragraph and return the Left() of its first child box.
+ // The paragraph contains the given string with keyword at ichMin..ichLim aligned at dmpAlign.
+ int LayoutConcParaAndGetFirstBoxLeft(const OLECHAR * pszText, int cch,
+ int ichMin, int ichLim, int dmpAlign, int textAlign)
+ {
+ HVO hvoPara = 1;
+ ITsStringPtr qtss;
+ CheckHr(m_qtsf->MakeStringRgch(pszText, cch, g_wsEng, &qtss));
+ CheckHr(m_qcda->CacheStringProp(hvoPara, kflidStTxtPara_Contents, qtss));
+
+ IVwViewConstructorPtr qvc;
+ qvc.Attach(NewObj ConcParaVc(ichMin, ichLim, dmpAlign, textAlign));
+ m_qrootb->SetRootObject(hvoPara, qvc, 1, NULL);
+ HRESULT hr = m_qrootb->Layout(m_qvg32, 300);
+ unitpp::assert_true("ConcPara layout succeeded", hr == S_OK);
+
+ VwParagraphBox * ppbox = dynamic_cast(m_qrootb->FirstBox());
+ unitpp::assert_true("Root contains a paragraph box", ppbox != NULL);
+ VwBox * pFirstChild = ppbox->FirstBox();
+ unitpp::assert_true("Paragraph has a child box", pFirstChild != NULL);
+
+ int result = pFirstChild->Left();
+
+ // Clean up so we can reuse the root box
+ m_qrootb->Close();
+ VwRootBox::CreateCom(NULL, IID_IVwRootBox, (void **) &m_qrootb);
+ m_qrootb->putref_DataAccess(m_qsda);
+ m_qrootb->putref_RenderEngineFactory(m_qref);
+ m_qrootb->putref_TsStrFactory(m_qtsf);
+ m_qrootb->SetSite(m_qdrs);
+
+ return result;
+ }
+
+ // Tests that DoSpecialAlignment uses the right edge of the keyword
+ // when the paragraph alignment is ktalRight, and the left edge when
+ // the alignment is ktalLeft. These should produce different box positions
+ // because the keyword has nonzero width.
+ void testConcParaAlignment_RightVsLeftEdge()
+ {
+ // Use a string where the keyword ("keyword") is in the middle.
+ // "Before keyword after end" - keyword at positions 7..14
+ const OLECHAR text[] = L"Before keyword after end";
+ int cch = (int)wcslen(text);
+ int ichMin = 7; // start of "keyword"
+ int ichLim = 14; // end of "keyword"
+ int dmpAlign = 36000; // 0.5 inch alignment point
+
+ int leftWithLeftAlign = LayoutConcParaAndGetFirstBoxLeft(
+ text, cch, ichMin, ichLim, dmpAlign, ktalLeft);
+ int leftWithRightAlign = LayoutConcParaAndGetFirstBoxLeft(
+ text, cch, ichMin, ichLim, dmpAlign, ktalRight);
+
+ // With left alignment, the left edge of "keyword" is placed at dmpAlign.
+ // With right alignment, the right edge of "keyword" is placed at dmpAlign.
+ // Since the keyword has nonzero width, the first child box should be
+ // shifted further left in the right-aligned case (by the width of the keyword).
+ unitpp::assert_true(
+ "Right-aligned conc para should shift boxes further left than left-aligned",
+ leftWithRightAlign < leftWithLeftAlign);
+ }
+
public:
TestVwParagraphBox();
diff --git a/Src/views/VwTextBoxes.cpp b/Src/views/VwTextBoxes.cpp
index 91a036c915..c3c55766dd 100644
--- a/Src/views/VwTextBoxes.cpp
+++ b/Src/views/VwTextBoxes.cpp
@@ -10055,7 +10055,11 @@ void VwConcParaBox::DoSpecialAlignment(IVwGraphics * pvg)
return; // ignore any problems here, just don't align.
}
- int dxsAlign = rgxdLefts[0] + psbox->Left();
+ int dxsAlign;
+ if (Style()->ParaAlign() == ktalRight)
+ dxsAlign = rgxdRights[cxd - 1] + psbox->Left();
+ else
+ dxsAlign = rgxdLefts[0] + psbox->Left();
int dxsGoalAlign = MulDiv(m_dmpAlign, dxsInch, kdzmpInch);
int dxsAdjust = dxsGoalAlign - dxsAlign;
// Now adjust all the box lefts by this amount