Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions Src/LexText/Interlinear/ITextDllTests/RawTextPaneTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright (c) 2026 SIL International
// This software is licensed under the LGPL, version 2.1 or later
// (http://www.gnu.org/licenses/lgpl-2.1.html)

using System.Windows.Forms;
using System.Xml;
using NUnit.Framework;
using SIL.LCModel;
using SIL.LCModel.Core.Text;
using SIL.LCModel.Infrastructure;
using XCore;

namespace SIL.FieldWorks.IText
{
/// <summary>
/// Tests that the Baseline pane's ShowInvisibleSpaces and ClickInvisibleSpace handlers
/// apply each other's effect directly instead of broadcasting the other property.
/// </summary>
[TestFixture]
public class RawTextPaneTests : MemoryOnlyBackendProviderTestBase
{
private IStText m_stText;
private Mediator m_mediator;
private PropertyTable m_propertyTable;
private TestRawTextPane m_pane;

[OneTimeSetUp]
public override void FixtureSetup()
{
base.FixtureSetup();
NonUndoableUnitOfWorkHelper.Do(Cache.ActionHandlerAccessor, () =>
{
m_stText = Cache.ServiceLocator.GetInstance<IStTextFactory>().Create();
Cache.ServiceLocator.GetInstance<ITextFactory>().Create().ContentsOA = m_stText;
m_stText.AddNewTextPara(null).Contents =
TsStringUtils.MakeString("xxxa xxxb xxxc.", Cache.DefaultVernWs);
});
}

[SetUp]
public override void TestSetup()
{
base.TestSetup();
m_mediator = new Mediator();
m_mediator.AddColleague(new ListeningColleague());
m_propertyTable = new PropertyTable(m_mediator);
// Reading a missing property stores and broadcasts its default
m_propertyTable.SetProperty("ShowInvisibleSpaces", false, false);
m_propertyTable.SetProperty("ClickInvisibleSpace", false, false);
m_pane =new TestRawTextPane(Cache, m_mediator, m_propertyTable, m_stText.Hvo);
}

[TearDown]
public override void TestTearDown()
{
m_pane.Dispose();
m_propertyTable.Dispose();
m_mediator.Dispose();
base.TestTearDown();
}

[Test]
public void ClickInvisibleSpaceOn_SpacesHidden_ShowsSpacesWithoutBroadcast()
{
// SUT
ChooseMenuItem("ClickInvisibleSpace", true);

Assert.That(m_propertyTable.GetBoolProperty("ShowInvisibleSpaces", false), Is.True);
Assert.That(m_pane.SpacesShown, Is.True, "the pane must show the spaces itself");
Assert.That(m_mediator.JobItems, Is.Zero, "ShowInvisibleSpaces must change without a broadcast");
}

[Test]
public void ShowInvisibleSpacesOff_ClickInsertsOn_EndsClickInsertsWithoutBroadcast()
{
ChooseMenuItem("ShowInvisibleSpaces", true);
ChooseMenuItem("ClickInvisibleSpace", true);

// SUT
ChooseMenuItem("ShowInvisibleSpaces", false);

Assert.That(m_propertyTable.GetBoolProperty("ClickInvisibleSpace", true), Is.False);
Assert.That(m_pane.SpacesShown, Is.False);
Assert.That(m_mediator.JobItems, Is.Zero, "ClickInvisibleSpace must change without a broadcast");

// The pane must also have recorded that click-insert is off, or turning it on
// again would be taken for no change and leave the spaces hidden.
ChooseMenuItem("ClickInvisibleSpace", true);
Assert.That(m_propertyTable.GetBoolProperty("ShowInvisibleSpaces", false), Is.True);
Assert.That(m_pane.SpacesShown, Is.True);
}

/// <summary>
/// Sets the property as the menu item's click does and delivers the broadcast to the
/// pane, so a test only counts the broadcasts the pane makes itself.
/// </summary>
private void ChooseMenuItem(string property, bool value)
{
m_propertyTable.SetProperty(property, value, false);
m_pane.OnPropertyChanged(property);
}

/// <summary>
/// A colleague registered so the Mediator queues broadcasts: with no colleague it
/// discards them before queueing, and the job count would prove nothing.
/// </summary>
private sealed class ListeningColleague : IxCoreColleague
{
public void Init(Mediator mediator, PropertyTable propertyTable, XmlNode configurationParameters) { }
public IxCoreColleague[] GetMessageTargets() => new IxCoreColleague[] { this };
public bool ShouldNotCall => false;
public int Priority => (int)ColleaguePriority.High;
}

/// <summary>
/// A laid-out pane on the given text with an insertion point, so a redraw can keep
/// the selection.
/// </summary>
private sealed class TestRawTextPane : RawTextPane
{
internal TestRawTextPane(LcmCache cache, Mediator mediator, PropertyTable propertyTable, int hvoText)
{
Cache = cache;
m_mediator = mediator;
m_propertyTable = propertyTable;
Width = 300;
Height = 300;
SetRoot(hvoText);
OnLayout(new LayoutEventArgs(this, "Bounds"));
RootBox.MakeSimpleSel(true, true, false, true);
}

internal bool SpacesShown => ((ShowSpaceDecorator)RootBox.DataAccess).ShowSpaces;
}
}
}
45 changes: 30 additions & 15 deletions Src/LexText/Interlinear/RawTextPane.cs
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,7 @@ public override void OnPropertyChanged(string name)
break;
case "ShowInvisibleSpaces":
newVal = ShowInvisibleSpaces;
if (newVal != m_showSpaceDa.ShowSpaces)
{
m_showSpaceDa.ShowSpaces = newVal;
var saveSelection = SelectionHelper.Create(this);
m_rootb.Reconstruct();
saveSelection.SetSelection(true);
}
ApplyShowInvisibleSpaces(newVal);
if (!newVal && ClickInvisibleSpace)
TurnOffClickInvisibleSpace();
break;
Expand All @@ -380,20 +374,41 @@ private void SetParaToReparse(IStTxtPara para)
NonUndoableUnitOfWorkHelper.Do(Cache.ActionHandlerAccessor, () => para.ParseIsCurrent = false);
}

/// <summary>
/// Turns on ShowInvisibleSpaces and redraws here; the set does not broadcast,
/// because this pane is its only listener.
/// </summary>
private void TurnOnShowInvisibleSpaces()
{
if (m_propertyTable != null)
{
m_propertyTable.SetProperty("ShowInvisibleSpaces", true, true);
}
if (m_propertyTable == null)
return;
m_propertyTable.SetProperty("ShowInvisibleSpaces", true, false);
ApplyShowInvisibleSpaces(true);
}

/// <summary>
/// Turns off ClickInvisibleSpace and records it here; the set does not broadcast,
/// because this pane is its only listener.
/// </summary>
private void TurnOffClickInvisibleSpace()
{
if (m_propertyTable != null)
{
m_propertyTable.SetProperty("ClickInvisibleSpace", false, true);
}
if (m_propertyTable == null)
return;
m_propertyTable.SetProperty("ClickInvisibleSpace", false, false);
m_fClickInsertsZws = false;
}

/// <summary>
/// Redraws the text, keeping the selection, when invisible spaces are shown or hidden.
/// </summary>
private void ApplyShowInvisibleSpaces(bool show)
{
if (show == m_showSpaceDa.ShowSpaces)
return;
m_showSpaceDa.ShowSpaces = show;
var saveSelection = SelectionHelper.Create(this);
m_rootb.Reconstruct();
saveSelection?.SetSelection(true);
}

private bool ShowInvisibleSpaces
Expand Down
Loading