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
8 changes: 8 additions & 0 deletions src/LogExpert.Core/Config/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ namespace LogExpert.Core.Config;
public class Preferences
{
private SelectionHighlightSettings _selectionHighlight = new();
private int[] _highlightCustomColors = [];

public SelectionHighlightSettings SelectionHighlight
{
get => _selectionHighlight;
set => _selectionHighlight = value ?? new();
}

/// <summary>Custom-color palette shared by the Highlight Entry color pickers, as ColorDialog.CustomColors values.</summary>
public int[] HighlightCustomColors
{
get => _highlightCustomColors;
set => _highlightCustomColors = value ?? [];
}

/// <summary>
/// List of highlight groups for syntax highlighting and text coloring.
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/LogExpert.Resources/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,9 @@ Ein ausgewähltes Tool erscheint in der Iconbar. Alle anderen verfügbaren Tools
<data name="HighlightDialog_UI_Button_Copy" xml:space="preserve">
<value>Kopieren</value>
</data>
<data name="HighlightDialog_UI_Button_CopyEntry" xml:space="preserve">
<value>Ko&amp;pieren</value>
</data>
<data name="HighlightDialog_UI_Button_DeleteGroup" xml:space="preserve">
<value>Gruppe löschen</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,9 @@ Checked tools will appear in the icon bar. All other tools are available in the
<data name="HighlightDialog_UI_Button_Copy" xml:space="preserve">
<value>&amp;Copy</value>
</data>
<data name="HighlightDialog_UI_Button_CopyEntry" xml:space="preserve">
<value>Co&amp;py</value>
</data>
<data name="HighlightDialog_UI_Button_DeleteGroup" xml:space="preserve">
<value>Delete group</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions src/LogExpert.Resources/Resources.zh-CN.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,9 @@
<data name="HighlightDialog_UI_Button_Copy" xml:space="preserve">
<value>复制(&amp;C)</value>
</data>
<data name="HighlightDialog_UI_Button_CopyEntry" xml:space="preserve">
<value>复制(&amp;P)</value>
</data>
<data name="HighlightDialog_UI_Button_DeleteGroup" xml:space="preserve">
<value>删除分组</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using LogExpert.Core.Config;

using Newtonsoft.Json;

using NUnit.Framework;

namespace LogExpert.Tests.ConfigManagerTests;

[TestFixture]
public class HighlightCustomColorsTests
{
[Test]
public void Palette_SurvivesRoundTrip ()
{
var preferences = new Preferences { HighlightCustomColors = [0x00123456, 0x00FFFFFF] };

var restored = JsonConvert.DeserializeObject<Preferences>(JsonConvert.SerializeObject(preferences));

Assert.That(restored.HighlightCustomColors, Is.EqualTo(new[] { 0x00123456, 0x00FFFFFF }));
}

[Test]
public void OlderSettings_LoadWithAnEmptyPalette ()
{
var preferences = JsonConvert.DeserializeObject<Preferences>("{}");

Assert.That(preferences.HighlightCustomColors, Is.Empty);
}

[Test]
public void NullPalette_BecomesEmpty ()
{
var preferences = JsonConvert.DeserializeObject<Preferences>("{\"HighlightCustomColors\":null}");

Assert.That(preferences.HighlightCustomColors, Is.Empty);
}
}
216 changes: 216 additions & 0 deletions src/LogExpert.UI.Tests/Dialogs/HighlightDialogEntryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using System.Reflection;

using LogExpert.Core.Classes.Highlight;
using LogExpert.Core.Config;
using LogExpert.Core.Entities;
using LogExpert.Core.Interfaces;
using LogExpert.Dialogs;
using LogExpert.UI.Dialogs.Highlight;

using Moq;

using NUnit.Framework;

namespace LogExpert.UI.Tests.Dialogs;

[TestFixture]
[Apartment(ApartmentState.STA)]
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public class HighlightDialogEntryTests
{
private Settings _settings;
private HighlightDialog _dialog;
private HighlightEntry _source;

[SetUp]
public void SetUp ()
{
_settings = new Settings();
_settings.Preferences.HighlightCustomColors = [0x00102030];
var config = new Mock<IConfigManager>();
_ = config.SetupGet(c => c.Settings).Returns(_settings);

_source = new HighlightEntry
{
SearchText = "ERR.*",
IsRegex = true,
IsCaseSensitive = true,
ForegroundColor = Color.Red,
BackgroundColor = Color.Yellow,
IsBold = true,
IsSetBookmark = true,
BookmarkComment = "note",
IsActionEntry = true,
ActionEntry = new ActionEntry { PluginName = "plugin", ActionParam = "param" },
AlertOnHit = true,
CooldownSeconds = 7,
};

_dialog = new HighlightDialog(config.Object)
{
HighlightGroupList = [new HighlightGroup { GroupName = "G", HighlightEntryList = [_source] }],
PreSelectedGroupName = "G",
};
_dialog.Show();
// Shown, which fills the entry list, is posted rather than raised by Show().
Application.DoEvents();
// The dialog edits clones of the groups it is given.
_source = Entries[0];
}

[TearDown]
public void TearDown ()
{
_dialog.Dispose();
}

private List<HighlightEntry> Entries => _dialog.HighlightGroupList[0].HighlightEntryList;

private ListBox List => Find<ListBox>("listBoxHighlight");

[Test]
public void Copy_IsDisabledWithoutASelectedEntry ()
{
List.SelectedIndex = -1;
Assert.That(Find<Button>("btnCopy").Enabled, Is.False);

List.SelectedIndex = 0;
Assert.That(Find<Button>("btnCopy").Enabled, Is.True);
}

[Test]
public void CopyAccepted_AppendsOneIndependentEntryAndSelectsIt ()
{
HighlightEntry edited = null;
_dialog.ShowEntryDialog = editor =>
{
edited = EditedEntry(editor);
return ClickOk(editor);
};
List.SelectedIndex = 0;

Find<Button>("btnCopy").PerformClick();

Assert.That(Entries, Has.Count.EqualTo(2));
var copy = Entries[1];
Assert.Multiple(() =>
{
Assert.That(copy, Is.SameAs(edited));
Assert.That(copy, Is.Not.SameAs(_source));
Assert.That(List.SelectedItem, Is.SameAs(copy));
Assert.That(copy.SearchText, Is.EqualTo("ERR.*"));
Assert.That(copy.IsRegex, Is.True);
Assert.That(copy.IsCaseSensitive, Is.True);
Assert.That(copy.ForegroundColor.ToArgb(), Is.EqualTo(Color.Red.ToArgb()));
Assert.That(copy.BackgroundColor.ToArgb(), Is.EqualTo(Color.Yellow.ToArgb()));
Assert.That(copy.IsBold, Is.True);
Assert.That(copy.IsSetBookmark, Is.True);
Assert.That(copy.BookmarkComment, Is.EqualTo("note"));
Assert.That(copy.IsActionEntry, Is.True);
Assert.That(copy.ActionEntry.PluginName, Is.EqualTo("plugin"));
Assert.That(copy.ActionEntry.ActionParam, Is.EqualTo("param"));
Assert.That(copy.AlertOnHit, Is.True);
Assert.That(copy.CooldownSeconds, Is.EqualTo(7));
});

copy.ActionEntry.ActionParam = "changed";
copy.SearchText = "changed";

Assert.That(_source.ActionEntry.ActionParam, Is.EqualTo("param"));
Assert.That(_source.SearchText, Is.EqualTo("ERR.*"));
}

[Test]
public void CopyEditedInTheEditor_LeavesTheSourceUnchanged ()
{
_dialog.ShowEntryDialog = editor =>
{
editor.Controls.Find("_textBoxSearchString", true).Single().Text = "WARN";
((CheckBox)editor.Controls.Find("_checkBoxBold", true).Single()).Checked = false;
return ClickOk(editor);
};
List.SelectedIndex = 0;

Find<Button>("btnCopy").PerformClick();

Assert.That(Entries[1].SearchText, Is.EqualTo("WARN"));
Assert.That(Entries[1].IsBold, Is.False);
Assert.That(_source.SearchText, Is.EqualTo("ERR.*"));
Assert.That(_source.IsBold, Is.True);
}

[Test]
public void CopyCancelled_AddsNothing ()
{
_dialog.ShowEntryDialog = _ => DialogResult.Cancel;
List.SelectedIndex = 0;

Find<Button>("btnCopy").PerformClick();

Assert.That(Entries, Is.EqualTo(new[] { _source }));
Assert.That(List.Items, Has.Count.EqualTo(1));
}

[Test]
public void Palette_StartsFromThePreferencesWithoutAliasingThem ()
{
Assert.That(_dialog.CustomColors, Is.EqualTo(new[] { 0x00102030 }));
Assert.That(_dialog.CustomColors, Is.Not.SameAs(_settings.Preferences.HighlightCustomColors));
}

[Test]
public void EditorOk_SharesItsPaletteWithTheNextEditor ()
{
int[] received = null;
_dialog.ShowEntryDialog = editor =>
{
editor.CustomColors = [0x00ABCDEF];
return DialogResult.OK;
};
List.SelectedIndex = 0;
Find<Button>("btnEdit").PerformClick();

_dialog.ShowEntryDialog = editor =>
{
received = editor.CustomColors;
return DialogResult.Cancel;
};
Find<Button>("btnAdd").PerformClick();

Assert.That(received, Is.EqualTo(new[] { 0x00ABCDEF }));
Assert.That(_dialog.CustomColors, Is.EqualTo(new[] { 0x00ABCDEF }));
Assert.That(_settings.Preferences.HighlightCustomColors, Is.EqualTo(new[] { 0x00102030 }));
}

[Test]
public void EditorCancel_DiscardsItsPalette ()
{
_dialog.ShowEntryDialog = editor =>
{
editor.CustomColors = [0x00ABCDEF];
return DialogResult.Cancel;
};
List.SelectedIndex = 0;

Find<Button>("btnCopy").PerformClick();

Assert.That(_dialog.CustomColors, Is.EqualTo(new[] { 0x00102030 }));
}

private static HighlightEntry EditedEntry (HighlightEntryDialog editor)
{
return (HighlightEntry)typeof(HighlightEntryDialog).GetField("_entry", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(editor);
}

private static DialogResult ClickOk (HighlightEntryDialog editor)
{
_ = typeof(HighlightEntryDialog).GetMethod("OnOkClick", BindingFlags.Instance | BindingFlags.NonPublic)!
.Invoke(editor, [editor, EventArgs.Empty]);
return editor.DialogResult;
}

private T Find<T> (string name) where T : Control
{
return (T)_dialog.Controls.Find(name, true).Single();
}
}
63 changes: 63 additions & 0 deletions src/LogExpert.UI.Tests/Dialogs/HighlightDialogLayoutTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using LogExpert.Core.Config;
using LogExpert.Core.Interfaces;
using LogExpert.Dialogs;

using Moq;

using NUnit.Framework;

namespace LogExpert.UI.Tests.Dialogs;

[TestFixture]
[Apartment(ApartmentState.STA)]
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public class HighlightDialogLayoutTests
{
[Test]
public void EnlargingTheDialog_GrowsTheEntryListBothWays ()
{
using var dialog = CreateDialog();
dialog.Show();
var list = dialog.Controls.Find("listBoxHighlight", true).Single();
var before = list.Size;

dialog.Size = new Size(dialog.Width + 200, dialog.Height + 150);

Assert.That(list.Width, Is.EqualTo(before.Width + 200));
Assert.That(list.Height, Is.GreaterThan(before.Height + 100));
}

[TestCase(1f)]
[TestCase(1.5f)]
[TestCase(2f)]
public void AtTheMinimumSize_ControlsNeitherOverlapNorLeaveTheirContainer (float scale)
{
using var dialog = CreateDialog();
dialog.Scale(new SizeF(scale, scale));
dialog.Show();
dialog.Size = dialog.MinimumSize;

foreach (var name in new[] { "pnlBackground", "groupBoxGroups" })
{
var container = dialog.Controls.Find(name, true).Single();
var children = container.Controls.Cast<Control>().Where(c => c.Visible).ToList();

foreach (var child in children)
{
Assert.That(container.ClientRectangle.Contains(child.Bounds), Is.True, $"{child.Name} inside {name}");

foreach (var other in children.Where(o => o != child))
{
Assert.That(child.Bounds.IntersectsWith(other.Bounds), Is.False, $"{child.Name} overlaps {other.Name}");
}
}
}
}

private static HighlightDialog CreateDialog ()
{
var config = new Mock<IConfigManager>();
_ = config.SetupGet(c => c.Settings).Returns(new Settings());
return new HighlightDialog(config.Object) { HighlightGroupList = [] };
}
}
Loading
Loading