[Feat] Interactive Board (#258)

* interactive board

* change2

* fixes
This commit is contained in:
CaypenNow
2024-04-02 19:17:43 +05:00
committed by GitHub
parent 600d236ac2
commit b635d4efaf
21 changed files with 752 additions and 1 deletions

View File

@@ -0,0 +1,66 @@
using JetBrains.Annotations;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Utility;
using static Content.Shared._White.InteractiveBoard.SharedInteractiveBoardComponent;
namespace Content.Client._White.InteractiveBoard.UI;
[UsedImplicitly]
public sealed class InteractiveBoardBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private InteractiveBoardWindow? _window;
public InteractiveBoardBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = new InteractiveBoardWindow();
_window.OnClose += Close;
_window.Input.OnKeyBindDown += args =>
{
if (args.Function == EngineKeyFunctions.TextSubmit)
{
var text = Rope.Collapse(_window.Input.TextRope);
Input_OnTextEntered(text);
args.Handle();
}
};
if (EntMan.TryGetComponent<InteractiveBoardVisualsComponent>(Owner, out var visuals))
{
_window.InitVisuals(Owner, visuals);
}
_window.OpenCentered();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
_window?.Populate((InteractiveBoardBoundUserInterfaceState) state);
}
private void Input_OnTextEntered(string text)
{
SendMessage(new InteractiveBoardInputTextMessage(text));
if (_window != null)
{
_window.Input.TextRope = Rope.Leaf.Empty;
_window.Input.CursorPosition = new TextEdit.CursorPos(0, TextEdit.LineBreakBias.Top);
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
}

View File

@@ -0,0 +1,35 @@
using System.Numerics;
namespace Content.Client._White.InteractiveBoard.UI;
[RegisterComponent]
public sealed partial class InteractiveBoardVisualsComponent : Component
{
public string ImagePath = "/Textures/White/Interface/InteractiveBoard/interactiveboardbackground.png";
public Box2 BackgroundPatchMargin = default;
public Color BackgroundModulate = Color.White;
public bool BackgroundImageTile = false;
public Vector2 BackgroundScale = Vector2.One;
public string? HeaderImagePath;
public Color HeaderImageModulate = Color.White;
public Box2 HeaderMargin = default;
public string? ContentImagePath;
public Color ContentImageModulate = Color.White;
public Box2 ContentMargin = default;
public int ContentImageNumLines = 1;
public Color FontAccentColor = new(223, 223, 213);
public Vector2? MaxWritableArea = null;
}

View File

@@ -0,0 +1,28 @@
<controls:InteractiveBoardWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client._White.InteractiveBoard.UI"
MouseFilter="Stop" MinSize="500 500"
SetSize="600 650">
<BoxContainer Name="ContentsRoot" Orientation="Vertical">
<PanelContainer StyleClasses="AngleRect" VerticalAlignment="Top">
<TextureButton Name="CloseButton" StyleClasses="windowCloseButton" HorizontalAlignment="Right"/>
<Button Name="CopyButton" Text="Копировать текст" StyleClasses="windowCopyButton" HorizontalAlignment="Left"/>
</PanelContainer>
<PanelContainer Name="Background" StyleClasses="DefaultBorder" VerticalExpand="True" HorizontalExpand="True">
<ScrollContainer Name="ScrollingContents" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalExpand="True" VerticalExpand="True" HScrollEnabled="False">
<PanelContainer Name="Content" VerticalExpand="True" HorizontalExpand="True">
<BoxContainer Orientation="Vertical" VerticalAlignment="Stretch">
<TextureButton Name="HeaderImage" HorizontalAlignment="Center" VerticalAlignment="Top" MouseFilter="Ignore"/>
<Control Name="TextAlignmentPadding" VerticalAlignment="Top"/>
<RichTextLabel Name="BlankIndicator" StyleClasses="LabelSecondaryColor" VerticalAlignment="Top" HorizontalAlignment="Center"/>
<RichTextLabel StyleClasses="WrittenText" Name="WrittenTextLabel" VerticalAlignment="Stretch" Margin="4" HorizontalExpand="True"/>
<PanelContainer Name="InputContainer" StyleClasses="TransparentBorderedWindowPanel" MinHeight="100"
VerticalAlignment="Stretch" VerticalExpand="True" HorizontalExpand="True">
<TextEdit Name="Input" StyleClasses="LineEdit" Access="Public" />
</PanelContainer>
</BoxContainer>
</PanelContainer>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
</controls:InteractiveBoardWindow>

View File

@@ -0,0 +1,207 @@
using System.Numerics;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.RichText;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
using static Content.Shared._White.InteractiveBoard.SharedInteractiveBoardComponent;
namespace Content.Client._White.InteractiveBoard.UI
{
[GenerateTypedNameReferences]
public sealed partial class InteractiveBoardWindow : BaseWindow
{
private static readonly Color DefaultTextColor = new(255, 255, 255);
private const int MarginSize = 12;
private StyleBoxTexture _contentTex = new();
private float _contentLineScale = 1.0f;
private DragMode _allowedResizeModes = ~DragMode.None;
private readonly Type[] _allowedTags = new Type[]
{
typeof(BoldItalicTag),
typeof(BoldTag),
typeof(BulletTag),
typeof(ColorTag),
typeof(HeadingTag),
typeof(ItalicTag)
};
public InteractiveBoardWindow()
{
RobustXamlLoader.Load(this);
CloseButton.OnPressed += _ => Close();
CopyButton.OnPressed += _ => CopyToClipboard();
}
public void InitVisuals(EntityUid entity, InteractiveBoardVisualsComponent visuals)
{
var resCache = IoCManager.Resolve<IResourceCache>();
Background.ModulateSelfOverride = visuals.BackgroundModulate;
var backgroundImage = resCache.GetResource<TextureResource>(visuals.ImagePath);
{
var backgroundImageMode = visuals.BackgroundImageTile ? StyleBoxTexture.StretchMode.Tile : StyleBoxTexture.StretchMode.Stretch;
var backgroundPatchMargin = visuals.BackgroundPatchMargin;
Background.PanelOverride = new StyleBoxTexture
{
Texture = backgroundImage,
TextureScale = visuals.BackgroundScale,
Mode = backgroundImageMode,
PatchMarginLeft = backgroundPatchMargin.Left,
PatchMarginBottom = backgroundPatchMargin.Bottom,
PatchMarginRight = backgroundPatchMargin.Right,
PatchMarginTop = backgroundPatchMargin.Top
};
}
if (visuals.HeaderImagePath != null)
{
HeaderImage.TexturePath = visuals.HeaderImagePath;
HeaderImage.MinSize = HeaderImage.TextureNormal?.Size ?? Vector2.Zero;
}
HeaderImage.ModulateSelfOverride = visuals.HeaderImageModulate;
HeaderImage.Margin = new Thickness(visuals.HeaderMargin.Left, visuals.HeaderMargin.Top,
visuals.HeaderMargin.Right, visuals.HeaderMargin.Bottom);
Content.ModulateSelfOverride = visuals.ContentImageModulate;
WrittenTextLabel.ModulateSelfOverride = visuals.FontAccentColor;
var contentImage = visuals.ContentImagePath != null ? resCache.GetResource<TextureResource>(visuals.ContentImagePath) : null;
if (contentImage != null)
{
_contentTex = new StyleBoxTexture
{
Texture = contentImage,
Mode = StyleBoxTexture.StretchMode.Tile,
};
Content.PanelOverride = _contentTex;
_contentLineScale = visuals.ContentImageNumLines;
}
Content.Margin = new Thickness(
visuals.ContentMargin.Left, visuals.ContentMargin.Top,
visuals.ContentMargin.Right, visuals.ContentMargin.Bottom);
if (visuals.MaxWritableArea != null)
{
var a = (Vector2)visuals.MaxWritableArea;
ScrollingContents.MinSize = Vector2.Zero;
ScrollingContents.MinSize = a;
if (a.X > 0.0f)
{
ScrollingContents.MaxWidth = a.X;
_allowedResizeModes &= ~(DragMode.Left | DragMode.Right);
SetWidth = float.NaN;
}
if (a.Y > 0.0f)
{
ScrollingContents.MaxHeight = a.Y;
_allowedResizeModes &= ~(DragMode.Top | DragMode.Bottom);
SetHeight = float.NaN;
}
}
}
protected override void Draw(DrawingHandleScreen handle)
{
if (WrittenTextLabel.TryGetStyleProperty<Font>("font", out var font))
{
float fontLineHeight = font.GetLineHeight(1.0f);
_contentTex.ExpandMarginTop = font.GetDescent(UIScale);
var scaleY = _contentLineScale * fontLineHeight / _contentTex.Texture?.Height ?? fontLineHeight;
_contentTex.TextureScale = new Vector2(1, scaleY);
{
var headerHeight = HeaderImage.Size.Y + HeaderImage.Margin.Top + HeaderImage.Margin.Bottom;
var headerInLines = headerHeight / (fontLineHeight * _contentLineScale);
var paddingRequiredInLines = (float)Math.Ceiling(headerInLines) - headerInLines;
var verticalMargin = fontLineHeight * paddingRequiredInLines * _contentLineScale;
TextAlignmentPadding.Margin = new Thickness(0.0f, verticalMargin, 0.0f, 0.0f);
}
}
base.Draw(handle);
}
public void Populate(InteractiveBoardBoundUserInterfaceState state)
{
var isEditing = state.Mode == InteractiveBoardAction.Write;
var wasEditing = InputContainer.Visible;
InputContainer.Visible = isEditing;
var msg = new FormattedMessage();
msg.AddMarkupPermissive(state.Text);
var shouldCopyText = 0 == Input.TextLength && 0 != state.Text.Length;
if (!wasEditing || shouldCopyText)
{
Input.TextRope = Rope.Leaf.Empty;
Input.CursorPosition = new TextEdit.CursorPos();
Input.InsertAtCursor(state.Text);
}
WrittenTextLabel.SetMessage(msg, _allowedTags, DefaultTextColor);
WrittenTextLabel.Visible = !isEditing && state.Text.Length > 0;
BlankIndicator.Visible = !isEditing && state.Text.Length == 0;
}
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
{
var mode = DragMode.None;
if (relativeMousePos.Y < MarginSize)
{
mode |= DragMode.Top;
}
else if (relativeMousePos.Y > Size.Y - MarginSize)
{
mode |= DragMode.Bottom;
}
if (relativeMousePos.X < MarginSize)
{
mode |= DragMode.Left;
}
else if (relativeMousePos.X > Size.X - MarginSize)
{
mode |= DragMode.Right;
}
if((mode & _allowedResizeModes) == DragMode.None)
{
return DragMode.Move;
}
return mode & _allowedResizeModes;
}
private void CopyToClipboard()
{
if (WrittenTextLabel.GetMessage() == null)
return;
if (InputContainer.Visible)
{
InputContainer.Visible = false;
Input.Editable = true;
WrittenTextLabel.Visible = true;
return;
}
InputContainer.Visible = true;
Input.Editable = false;
WrittenTextLabel.Visible = false;
}
}
}