Skip to content

Commit

Permalink
#167 Partial text enhancements
Browse files Browse the repository at this point in the history
Use # as a shape symbol.
Align text to top.
Do not rerender shaped text.
Send whole words to client.

All these things remove partial text flickering.
  • Loading branch information
VladimirKhil committed Dec 13, 2023
1 parent 3b3ee31 commit e9d8cb6
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 33 deletions.
5 changes: 4 additions & 1 deletion src/Common/SIUI.ViewModel/TableInfoViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,10 @@ public double Volume

private bool _partialText = false;

public bool PartialText { get => _partialText; set { _partialText = value; OnPropertyChanged(); } }
/// <summary>
/// Is text displayed partially.
/// </summary>
public bool PartialText { get => _partialText; set { if (_partialText != value) { _partialText = value; OnPropertyChanged(); } } }

private LayoutMode _layoutMode = LayoutMode.Simple;

Expand Down
46 changes: 31 additions & 15 deletions src/Common/SIUI/Behaviors/FillManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,17 @@
namespace SIUI.Behaviors;

/// <summary>
/// Поведение, изменяющее размер шрифта текста до максимальной величины, при которой он целиком умещается на экране
/// Updates text font size dynamically so the text has maximum available font size and fits inside parent.
/// </summary>
public static class FillManager
{
private static readonly DependencyPropertyDescriptor TextDescriptor = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
private static readonly DependencyPropertyDescriptor FontFamilyDescriptor = DependencyPropertyDescriptor.FromProperty(TextBlock.FontFamilyProperty, typeof(TextBlock));

public static bool GetFill(DependencyObject obj)
{
return (bool)obj.GetValue(FillProperty);
}
public static bool GetFill(DependencyObject obj) => (bool)obj.GetValue(FillProperty);

public static void SetFill(DependencyObject obj, bool value)
{
obj.SetValue(FillProperty, value);
}
public static void SetFill(DependencyObject obj, bool value) => obj.SetValue(FillProperty, value);

// Using a DependencyProperty as the backing store for Fill. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FillProperty =
DependencyProperty.RegisterAttached("Fill", typeof(bool), typeof(FillManager), new UIPropertyMetadata(false, OnFillChanged));

Expand Down Expand Up @@ -61,6 +54,13 @@ public static void OnFillChanged(DependencyObject d, DependencyPropertyChangedEv
public static readonly DependencyProperty HandlerProperty =
DependencyProperty.RegisterAttached("Handler", typeof(SizeChangedEventHandler), typeof(FillManager), new UIPropertyMetadata(null));

public static bool GetHandleTextChange(DependencyObject obj) => (bool)obj.GetValue(HandleTextChangeProperty);

public static void SetHandleTextChange(DependencyObject obj, bool value) => obj.SetValue(HandleTextChangeProperty, value);

public static readonly DependencyProperty HandleTextChangeProperty =
DependencyProperty.RegisterAttached("HandleTextChange", typeof(bool), typeof(FillManager), new PropertyMetadata(true));

private static void TextBlock_Loaded(object sender, RoutedEventArgs e)
{
var textBlock = (TextBlock)sender;
Expand All @@ -78,7 +78,7 @@ private static void TextBlock_Loaded(object sender, RoutedEventArgs e)
parent.SizeChanged += handler;
MeasureFontSize(sender, EventArgs.Empty);

TextDescriptor.AddValueChanged(textBlock, MeasureFontSize);
TextDescriptor.AddValueChanged(textBlock, MeasureFontSizeOnTextChange);
FontFamilyDescriptor.AddValueChanged(textBlock, MeasureFontSize);
}

Expand All @@ -95,7 +95,7 @@ private static void TextBlock_Unloaded(object sender, RoutedEventArgs e)

textBlock.ClearValue(HandlerProperty);

TextDescriptor.RemoveValueChanged(textBlock, MeasureFontSize);
TextDescriptor.RemoveValueChanged(textBlock, MeasureFontSizeOnTextChange);
FontFamilyDescriptor.RemoveValueChanged(textBlock, MeasureFontSize);
}

Expand All @@ -117,16 +117,32 @@ private static void TextBlock_Unloaded(object sender, RoutedEventArgs e)

public static void OnInterlinyageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) => MeasureFontSize(d, EventArgs.Empty);

private static void MeasureFontSizeOnTextChange(object? sender, EventArgs e)
{
if (sender is not DependencyObject dependencyObject)
{
return;
}

var handleTextChange = GetHandleTextChange(dependencyObject);

if (!handleTextChange)
{
return;
}

MeasureFontSize(sender, e);
}

/// <summary>
/// Задаёт как можно больший размер шрифта текстового блока исходя из доступной для него области
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void MeasureFontSize(object? sender, EventArgs e)
{
var textBlock = sender as TextBlock;

if (VisualTreeHelper.GetParent(textBlock) is not FrameworkElement parent)
if (sender is not TextBlock textBlock
|| VisualTreeHelper.GetParent(textBlock) is not FrameworkElement parent)
{
return;
}
Expand Down
4 changes: 3 additions & 1 deletion src/Common/SIUI/Table.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,10 @@
Margin="10"
TextWrapping="Wrap"
HorizontalAlignment="Left"
VerticalAlignment="Top"
TextAlignment="Left"
lb:QuestionReading.IsAttachedPartial="True">
lb:QuestionReading.IsAttachedPartial="True"
lb:FillManager.HandleTextChange="False">
<TextBlock.CacheMode>
<BitmapCache EnableClearType="True" SnapsToDevicePixels="False" RenderAtScale="1.5" />
</TextBlock.CacheMode>
Expand Down
18 changes: 14 additions & 4 deletions src/SICore/SICore/Clients/Game/GameLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public sealed class GameLogic : Logic<GameData>
/// </summary>
private const double PartialPrintFrequencyPerSecond = 0.5;

/// <summary>
/// Represents character used to form content shape.
/// </summary>
private const string ContentShapeCharacter = "#"; // '%' as an alternative

/// <summary>
/// Execution continuation.
/// </summary>
Expand Down Expand Up @@ -428,14 +433,13 @@ internal void OnContentScreenText(string text, bool waitForFinish, TimeSpan dura

if (_data.IsPartial)
{
// "и" symbol is used as an arbitrary symbol with medium width to define the question text shape
// It does not need to be localized
// ContentShapeCharacter symbol is used as an arbitrary symbol with medium width to define the question text shape
// Real question text is sent later and it sequentially replaces test shape
// Text shape is required to display partial question on the screen correctly
// (font size and number of lines must be calculated in the beginning to prevent UI flickers on question text growth)
var shape = Regex.Replace(text, "[^\r\n\t\f ]", "и");
var shape = Regex.Replace(text, "[^\r\n\t\f ]", ContentShapeCharacter);
_gameActions.SendMessageWithArgs(Messages.TextShape, shape);
_gameActions.SendMessageWithArgs(Messages.ContentShape, ContentPlacements.Screen, 0, ContentTypes.Text, shape);
_gameActions.SendMessageWithArgs(Messages.ContentShape, ContentPlacements.Screen, 0, ContentTypes.Text, shape.EscapeNewLines());

_data.Text = text;
_data.TextLength = 0;
Expand Down Expand Up @@ -2783,6 +2787,12 @@ private void PrintPartial()
text.Length - _data.TextLength,
(int)(_data.Settings.AppSettings.ReadingSpeed * PartialPrintFrequencyPerSecond)));

// Align to next space position
while (_data.TextLength + printingLength + 1 < text.Length && !char.IsWhiteSpace(text[_data.TextLength + printingLength]))
{
printingLength++;
}

var subText = text.Substring(_data.TextLength, printingLength);

_gameActions.SendMessageWithArgs(Messages.ContentAppend, ContentPlacements.Screen, 0, ContentTypes.Text, subText.EscapeNewLines());
Expand Down
2 changes: 1 addition & 1 deletion src/SICore/SICore/Clients/Viewer/IViewerLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public interface IViewerLogic : ILogic

void PrintGreeting();

void TextShape(string[] mparams);
void OnTextShape(string[] mparams) { }

void OnTimeChanged();

Expand Down
2 changes: 1 addition & 1 deletion src/SICore/SICore/Clients/Viewer/Viewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ await _client.Node.ConnectionsLock.WithLockAsync(() =>
break;

case Messages.TextShape:
_logic.TextShape(mparams);
_logic.OnTextShape(mparams);
break;

case Messages.Atom: // deprecated
Expand Down
5 changes: 0 additions & 5 deletions src/SICore/SICore/Clients/Viewer/ViewerComputerLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,6 @@ public void PrintGreeting()

}

public void TextShape(string[] mparams)
{

}

public void OnTimeChanged()
{

Expand Down
4 changes: 2 additions & 2 deletions src/SICore/SICore/Clients/Viewer/ViewerHumanLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ virtual public async void Choice()
}
}

public void TextShape(string[] mparams)
public void OnTextShape(string[] mparams)
{
var text = new StringBuilder();

Expand All @@ -606,7 +606,7 @@ public void TextShape(string[] mparams)
}
}

if (!TInfo.PartialText && TInfo.TStage == TableStage.Question)
if (TInfo.TStage == TableStage.Question)
{
// Toggle TStage change to reapply QuestionTemplateSelector template
TInfo.TStage = TableStage.Void;
Expand Down
4 changes: 2 additions & 2 deletions src/SICore/SIData/AppSettingsCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ public int MultimediaPort
private int _readingSpeed = DefaultReadingSpeed;

/// <summary>
/// Скорость чтения вопроса (символов в секунду)
/// Text reading speed (characters per second).
/// </summary>
[XmlAttribute]
[DefaultValue(DefaultReadingSpeed)]
public int ReadingSpeed
{
get => _readingSpeed;
set { _readingSpeed = value; OnPropertyChanged(); }
set { if (_readingSpeed != value) { _readingSpeed = value; OnPropertyChanged(); } }
}

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Expand Down
1 change: 0 additions & 1 deletion src/SICore/SIData/SIData.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
<None Include="key.snk" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.0" />
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
</ItemGroup>
</Project>

0 comments on commit e9d8cb6

Please sign in to comment.