Files
Delta-v/Content.Client/Access/UI/GroupedAccessLevelChecklist.xaml.cs
T
29e55b9464 Upstream Merge (June / July) attempt 2 electric boogaloo (#4607)
* Fix the sensor monitoring console forcing a GC every 3 seconds (#38146)

* Optimize sensor monitoring window graph drawing

* Add shared static Vector2 pool for all GraphView instances

* Address requested changes

* remove lock

* Update submodule to 264.0.0 (#38629)

* Toy/Plushie Inhands and Wearables (#38514)

* Fixed dependency injection and some other issues in a few places.

* More issue fixes

* patchfor broken NetworkConfiguratorLinkMenu (#38632)

fix

* Fix solutions flickering when transferring contents (#34838)

* Use Solution clones when applying SolutionComponent states

* Revert "Use Solution clones when applying SolutionComponent states"

This reverts commit 013fd111cf92b22562e00f98a7aaa49bc4b4ed62.

* Make Solution implement ICloneable and rename Clone method.

* Copy CanReact value when cloning a Solution

* Convert to IRobustCloneable

* Fix typos in guidebook: Buisness → Business (#38636)

* Fix typo in `MinorAntagonists.xml`

Buisness → Business

* Fix typo in `YourFirstCharacter.xml`

Buisness → Business

* Various Headphones Fixes and Tweaks (#38479)

* sprites, wearables

* neck

* icon-on sprite

* Cleanup prototype instantiation in `DamageTest` (#38639)

Cleanup prototype instantiation in DamageTest

* Cleanup prototype instantiation in `ExplosionSystem` (#38642)

Cleanup prototype instantiation in ExplosionSystem

* AddBodyPartCommand localization. (#38612)

commit

* fix ItemSlotsSystem debug assert (#38655)

* Allow the Command & Super door remotes to use the access of their user. (Re-creation of PR due to changes to game balance) (#35536)

* Added directional beacons (#38284)

* Added directional beacons

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

* Fixed names

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

---------

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

* Power stat and nuke codes commands get some LEC love. (#38585)

* commit

* requested changes.

* Dsay Dirty and Follow commands converted to LEC and localized. (#38666)

* commit

* whoopwhoopwhoop

* Retro laser sprite fix (#38676)

* Fixed everything except the icon

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

* fixed icon

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

---------

Signed-off-by: Nox38 <nebulousnox38@gmail.com>

* fix water coolers (#38681)

* Monochromacy typo fix (#38686)

* fixes the typo

* Fixed cloning looking for the trait, not the component, RE https://github.com/space-wizards/space-station-14/pull/38686#issuecomment-3025093504

* Apply suggestions from code review

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Validate `CloningSettingsPrototype`s (#38688)

* Validate CloningSettingsPrototypes

* Update Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Check EventComponents too

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Add test of objective-related console commands (#36400)

* Add test of objective add/list/remove commands

* Not sure why we're validating test prototypes, but sure

* We don't need a map

* Improvements and fixups for New Status Effect API (#38660)

* Predict healing and bloodstream (#38690)

* initial commit

* reapply 38126

* fix rootable

* someone missed an important minus sign here

* try this

* fix

* fix

* reenable crit hits

* cleanup

* fix status time dirtying

* fix

* camelCase

* Healing and bloodstream prediction fixes.

* Resolving Wizard casting recall on nuke disk making it impossible to disarm (#38661)

* Resolving Wizard Recall on Nuke disk making it impossible to disarm
- Adding a DisarmBomb case to nuke status update loop
- Changing a few methods and parameters to properly follow formatting standards
- Updating some names to follow camelCase

* Updating missed tag

* Reverting DataField change

Should prevent this preventative bugfix being a breaking change.

* Switch HSV to the default colorspace for character customization (#38434)

* Made HSV default for character editor

* Adds/fixes comments to HSV defaulting

* Added dropbox fix, potentially cursed

* Revert "Added dropbox fix, potentially cursed"

This reverts commit a709883366fbee813e839742125e70844672af29.

---------

Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com>

* Fix: Don't deploy foldables when clicking on items inside containers (#38709)

* Fix

* Apply suggestions from code review

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Pressure Relief Valve (#36708)

* initial system (this math is probably WRONG)

* General code cleanup and OnExamined support
(holy moly this code sucks)

* UICode and related events foundation
TODO:
- Actually write the XAML UI and the underlying system
- Un-shitcode the entire thing
- Actually test everything...

* Working UI code
TODO: Make predicted, as this certainly isn't predicted. Even though I said it was. It isn't.

* Remove one TODO for unshitcoding the examine code

* Add reminder
yea

* Make predicted (defenitely isn't)
(also defenitely isn't a copypaste from pressure pump code)

* It's predicted!
TODO:
- Give it snazzy predicted visuals!
- Have a different field for pressure entry, lest it gets bulldozed every UI update.

* Improve gas pressure relief valve UI
TODO: Reminder to reduce amount of dirties using deltafields

* Implement DirtyField prediction

* Entity<T> cleanup
A lot of Entity<T> conversions and lukewarm cleanup.

Also got caught copy pasting code in 4K UHD but it's not like you couldn't tell.

* More cleanup and comments

* Remove TODO comment on bulldozing window title

* """refactoring"""
- Move appearance out of shared and finally fix it. Pointless to predict appearance in this instance.
- More Entity<T> conversions because I like them.
- Move UI creation handling over entirely to the ActivatableUI system.
- Fix a hardcoded locale string (why????).

* Add visuals

* Revert debugging variable replacememt
yea

* Revert skissue

* Remove unused using directives and remove TODO

* Localize, cleanup, document

* Fix adminlogging discrepancy

* Add ability to construct, add guidebook entry

* Clear up comment

* Add guidebook tooltip to valve

* Convert GasPressureReliefValveBoundUserInterface declaration into primary constructor

* Adds more input handling and adds autofill on open

* Un-deepfry input validator shitcode
Genuinely what was I smoking

* improve visuals logic

* Refactor again
- Update math to the correct implementation
- Moved code that could be re-used in the future into a helper method under AtmosphereSystem.Gases.cs

* I'm sorry but I hate warnings

* Remove unused using directive in AtmosphereSystem.Gases.cs

* Review and cleanup

* Lukewarm UI glossup

* Maintainer for the upstream project btw

* Remove redundant state sets and messy logic

* Unduplicate valve updater code

* Redo UI (im sorry Slarti)

* run tests

* Test refactored UI messaging

* Second round of UI improvements
- God please find a way to improve this system. Feels bad.

* Update loop implementation

* Further predict UI

* Clear up SetToCurrentThreshold

* cleanup

* Update to master + pipe layers and bug fixes
want to run tests

* fixes

* Deploy rename pipebomb

* Documentation and requested changes

* Rename the method that wiggled away

* Undo rounding changes

* Fix comment

* Rename and cleanup

* Apply suggestions from code review

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Fix TextLinkTag (#32203)

* Adjust uplink buy button to be under item icon (#38596)

* Adjusted uplink buy button to be under item icon

* Put the discount subtext under the icon

* Indent fixes, added margin

---------

Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com>

* Scurrets - Audio Improvements (#38482)

* Scurret audio tuning

* Add new sfx

* Update sneezing sfx

* YAML support

* Rename a folder

* make telesci wreck easier (#37569)

rel

* Validate `ShaderPrototype` IDs (#38728)

* Convert all shader prototype string literals to protoids in overlays

* Convert more shader prototype literal strings to protoids

* Convert ValidatePrototypeId to ProtoId

* Later

* [BUGFIX] Fixed revenant malfunction ability not working properly only MediBots and Stasis bed (#38664)

* fixed

* clean up

* orks fix smart

* review fix 1

* more requested changes

* less cursed

* more descriptive description

* better wording

* Tiny Tiny Cleanup of the EyeClosingSystem. (#38734)

Update EyeClosingSystem.cs

* Fix human skin tone distribution (#38701)

* fix: Use PredictedQueueDel for gib spell (#38729)

* fix: don't default to uncharged sprite state for cells (#38730)

* Fix Hristov description - remove inaccurate technical specs (#38746)

- Removes inaccurate 'armor piercing 14.5mm shells' reference
- Replaces it with a more funny description, matching the style of the other snipers and guns
- Keeps ammunition type '.60 anti-materiel ammo' specification
- Fixes issue #38590

Co-authored-by: Arthur Fiorese de Andrade <aandrade@cmcxs.gov.br>

* feat: allow mopping evaporating puddles (#38743)

* Validate remaining `ProtoId` strings (#38747)

Validate remaining ProtoId strings

* Validate `ProtoId`s in tests (#38745)

* Convert string literals to protoids in Content.Tests

* Convert string literals to protoids or consts in Content.IntegrationTests

* Fix linter failures
Tricksy static using misled me

* Cleanup warning in StomachSystem (#38748)

you did not see this

* Vox scars (#38592)

* Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl

* Revert "Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl"

This reverts commit c73da55ba3b39ddf93b493aecd85604c54dd8a15.

* locale key fix

* Changed top surgery scar names to be more generalized

* Adjusted face scars

* Formatting fixes

---------

Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com>

* UnlockNode command to LEC. (#38751)

* commit

* Update UnlockNodeCommand.cs

* commit

* move command locale to its own file.

* Update Content.Server/Xenoarchaeology/Artifact/UnlockNodeCommand.cs

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Typofixes for figurine dialogue (#38737)

* Typofixes for figurine dialogue

* Forgot two

* Janitor Tool: Wire Brush (#38667)

* Wow! It's -brush-

* spacing.

* Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* fixed changes

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Minor escape menu UX improvements (#38650)

* fix: spellbooks can have infinite charges (#38376)

* fix: spellbooks can have infinite charges

* refactor: indicate infinite spellbook charges with null

Not sure if I like this much better...
2025-11-06 18:22:39 +00:00

452 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Access;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Linq;
using System.Numerics;
namespace Content.Client.Access.UI;
[GenerateTypedNameReferences]
public sealed partial class GroupedAccessLevelChecklist : BoxContainer
{
private static readonly ProtoId<AccessGroupPrototype> GeneralAccessGroup = "General";
[Dependency] private readonly IPrototypeManager _protoManager = default!;
private bool _isMonotone;
private string? _labelStyleClass;
// Access data
private HashSet<ProtoId<AccessGroupPrototype>> _accessGroups = new();
private HashSet<ProtoId<AccessLevelPrototype>> _accessLevels = new();
private HashSet<ProtoId<AccessLevelPrototype>> _activeAccessLevels = new();
// Button groups
private readonly ButtonGroup _accessGroupsButtons = new();
// Temp values
private int _accessGroupTabIndex = 0;
private bool _canInteract = false;
private List<AccessLevelPrototype> _accessLevelsForTab = new();
private readonly List<AccessLevelEntry> _accessLevelEntries = new();
private readonly Dictionary<AccessGroupPrototype, List<AccessLevelPrototype>> _groupedAccessLevels = new();
// Events
public event Action<HashSet<ProtoId<AccessLevelPrototype>>, bool>? OnAccessLevelsChangedEvent;
/// <summary>
/// Creates a UI control for changing access levels.
/// Access levels are organized under a list of tabs by their associated access group.
/// </summary>
public GroupedAccessLevelChecklist()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
private void ArrangeAccessControls()
{
// Create a list of known access groups with which to populate the UI
_groupedAccessLevels.Clear();
foreach (var accessGroup in _accessGroups)
{
if (!_protoManager.TryIndex(accessGroup, out var accessGroupProto))
continue;
_groupedAccessLevels.Add(accessGroupProto, new());
}
// Ensure that the 'general' access group is added to handle
// misc. access levels that aren't associated with any group
if (_protoManager.TryIndex(GeneralAccessGroup, out var generalAccessProto))
_groupedAccessLevels.TryAdd(generalAccessProto, new());
// Assign known access levels with their associated groups
foreach (var accessLevel in _accessLevels)
{
if (!_protoManager.TryIndex(accessLevel, out var accessLevelProto))
continue;
var assigned = false;
foreach (var (accessGroup, accessLevels) in _groupedAccessLevels)
{
if (!accessGroup.Tags.Contains(accessLevelProto.ID))
continue;
assigned = true;
_groupedAccessLevels[accessGroup].Add(accessLevelProto);
}
if (!assigned && generalAccessProto != null)
_groupedAccessLevels[generalAccessProto].Add(accessLevelProto);
}
// Remove access groups that have no assigned access levels
foreach (var (group, accessLevels) in _groupedAccessLevels)
{
if (accessLevels.Count == 0)
_groupedAccessLevels.Remove(group);
}
}
private bool TryRebuildAccessGroupControls()
{
AccessGroupList.DisposeAllChildren();
AccessLevelChecklist.DisposeAllChildren();
// No access level prototypes were assigned to any of the access level groups.
// Either the turret controller has no assigned access levels or their names were invalid.
if (_groupedAccessLevels.Count == 0)
return false;
// Reorder the access groups alphabetically
var orderedAccessGroups = _groupedAccessLevels.Keys.OrderBy(x => x.GetAccessGroupName()).ToList();
// Add group access buttons to the UI
foreach (var accessGroup in orderedAccessGroups)
{
var accessGroupButton = CreateAccessGroupButton();
// Button styling
if (_groupedAccessLevels.Count > 1)
{
if (AccessGroupList.ChildCount == 0)
accessGroupButton.AddStyleClass(StyleBase.ButtonOpenLeft);
else if (_groupedAccessLevels.Count > 1 && AccessGroupList.ChildCount == (_groupedAccessLevels.Count - 1))
accessGroupButton.AddStyleClass(StyleBase.ButtonOpenRight);
else
accessGroupButton.AddStyleClass(StyleBase.ButtonOpenBoth);
}
accessGroupButton.Pressed = _accessGroupTabIndex == orderedAccessGroups.IndexOf(accessGroup);
// Label text and styling
if (_labelStyleClass != null)
accessGroupButton.Label.SetOnlyStyleClass(_labelStyleClass);
var accessLevelPrototypes = _groupedAccessLevels[accessGroup];
var prefix = accessLevelPrototypes.All(x => _activeAccessLevels.Contains(x))
? "»"
: accessLevelPrototypes.Any(x => _activeAccessLevels.Contains(x))
? "›"
: " ";
var text = Loc.GetString(
"turret-controls-window-access-group-label",
("prefix", prefix),
("label", accessGroup.GetAccessGroupName())
);
accessGroupButton.Text = text;
// Button events
accessGroupButton.OnPressed += _ => OnAccessGroupChanged(accessGroupButton.GetPositionInParent());
AccessGroupList.AddChild(accessGroupButton);
}
// Adjust the current tab index so it remains in range
if (_accessGroupTabIndex >= _groupedAccessLevels.Count)
_accessGroupTabIndex = _groupedAccessLevels.Count - 1;
return true;
}
/// <summary>
/// Rebuilds the checkbox list for the access level controls.
/// </summary>
public void RebuildAccessLevelsControls()
{
AccessLevelChecklist.DisposeAllChildren();
_accessLevelEntries.Clear();
// No access level prototypes were assigned to any of the access level groups
// Either turret controller has no assigned access levels, or their names were invalid
if (_groupedAccessLevels.Count == 0)
return;
// Reorder the access groups alphabetically
var orderedAccessGroups = _groupedAccessLevels.Keys.OrderBy(x => x.GetAccessGroupName()).ToList();
// Get the access levels associated with the current tab
var selectedAccessGroupTabProto = orderedAccessGroups[_accessGroupTabIndex];
_accessLevelsForTab = _groupedAccessLevels[selectedAccessGroupTabProto];
_accessLevelsForTab = _accessLevelsForTab.OrderBy(x => x.GetAccessLevelName()).ToList();
// Add an 'all' checkbox as the first child of the list if it has more than one access level
// Toggling this checkbox on will mark all other boxes below it on/off
var allCheckBox = CreateAccessLevelCheckbox();
allCheckBox.Text = Loc.GetString("turret-controls-window-all-checkbox");
if (_labelStyleClass != null)
allCheckBox.Label.SetOnlyStyleClass(_labelStyleClass);
// Add the 'all' checkbox events
allCheckBox.OnPressed += args =>
{
SetCheckBoxPressedState(_accessLevelEntries, allCheckBox.Pressed);
var accessLevels = new HashSet<ProtoId<AccessLevelPrototype>>();
foreach (var accessLevel in _accessLevelsForTab)
{
accessLevels.Add(accessLevel);
}
OnAccessLevelsChangedEvent?.Invoke(accessLevels, allCheckBox.Pressed);
};
AccessLevelChecklist.AddChild(allCheckBox);
// Hide the 'all' checkbox if the tab has only one access level
var allCheckBoxVisible = _accessLevelsForTab.Count > 1;
allCheckBox.Visible = allCheckBoxVisible;
allCheckBox.Disabled = !_canInteract;
// Add any remaining missing access level buttons to the UI
foreach (var accessLevel in _accessLevelsForTab)
{
// Create the entry
var accessLevelEntry = new AccessLevelEntry(_isMonotone);
accessLevelEntry.AccessLevel = accessLevel;
accessLevelEntry.CheckBox.Text = accessLevel.GetAccessLevelName();
accessLevelEntry.CheckBox.Pressed = _activeAccessLevels.Contains(accessLevel);
accessLevelEntry.CheckBox.Disabled = !_canInteract;
if (_labelStyleClass != null)
accessLevelEntry.CheckBox.Label.SetOnlyStyleClass(_labelStyleClass);
// Set the checkbox linkage lines
var isEndOfList = _accessLevelsForTab.IndexOf(accessLevel) == (_accessLevelsForTab.Count - 1);
var lines = new List<(Vector2, Vector2)>
{
(new Vector2(0.5f, 0f), new Vector2(0.5f, isEndOfList ? 0.5f : 1f)),
(new Vector2(0.5f, 0.5f), new Vector2(1f, 0.5f)),
};
accessLevelEntry.UpdateCheckBoxLink(lines);
accessLevelEntry.CheckBoxLink.Visible = allCheckBoxVisible;
accessLevelEntry.CheckBoxLink.Modulate = !_canInteract ? Color.Gray : Color.White;
// Add checkbox events
accessLevelEntry.CheckBox.OnPressed += args =>
{
// If the checkbox and its siblings are checked, check the 'all' checkbox too
allCheckBox.Pressed = AreAllCheckBoxesPressed(_accessLevelEntries.Select(x => x.CheckBox));
OnAccessLevelsChangedEvent?.Invoke([accessLevelEntry.AccessLevel], accessLevelEntry.CheckBox.Pressed);
};
AccessLevelChecklist.AddChild(accessLevelEntry);
_accessLevelEntries.Add(accessLevelEntry);
}
// Press the 'all' checkbox if all others are pressed
allCheckBox.Pressed = AreAllCheckBoxesPressed(_accessLevelEntries.Select(x => x.CheckBox));
}
private bool AreAllCheckBoxesPressed(IEnumerable<CheckBox> checkBoxes)
{
foreach (var checkBox in checkBoxes)
{
if (!checkBox.Pressed)
return false;
}
return true;
}
private void SetCheckBoxPressedState(List<AccessLevelEntry> accessLevelEntries, bool pressed)
{
foreach (var accessLevelEntry in accessLevelEntries)
{
accessLevelEntry.CheckBox.Pressed = pressed;
}
}
/// <summary>
/// Provides the UI with a list of access groups using which list of tabs should be populated.
/// </summary>
public void SetAccessGroups(HashSet<ProtoId<AccessGroupPrototype>> accessGroups)
{
_accessGroups = accessGroups;
ArrangeAccessControls();
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
/// <summary>
/// Provides the UI with a list of access levels with which it can populate the currently selected tab.
/// </summary>
public void SetAccessLevels(HashSet<ProtoId<AccessLevelPrototype>> accessLevels)
{
_accessLevels = accessLevels;
ArrangeAccessControls();
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
/// <summary>
/// Sets which access level checkboxes should be marked on the UI.
/// </summary>
public void SetActiveAccessLevels(HashSet<ProtoId<AccessLevelPrototype>> activeAccessLevels)
{
_activeAccessLevels = activeAccessLevels;
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
/// <summary>
/// Sets whether the local player can interact with the checkboxes.
/// </summary>
public void SetLocalPlayerAccessibility(bool canInteract)
{
_canInteract = canInteract;
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
/// <summary>
/// Sets whether the UI should use monotone buttons and checkboxes.
/// </summary>
public void SetMonotone(bool monotone)
{
_isMonotone = monotone;
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
/// <summary>
/// Applies the specified style to the labels on the UI buttons and checkboxes.
/// </summary>
public void SetLabelStyleClass(string? styleClass)
{
_labelStyleClass = styleClass;
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
private void OnAccessGroupChanged(int newTabIndex)
{
if (newTabIndex == _accessGroupTabIndex)
return;
_accessGroupTabIndex = newTabIndex;
if (TryRebuildAccessGroupControls())
RebuildAccessLevelsControls();
}
private Button CreateAccessGroupButton()
{
var button = _isMonotone ? new MonotoneButton() : new Button();
button.ToggleMode = true;
button.Group = _accessGroupsButtons;
button.Label.HorizontalAlignment = HAlignment.Left;
return button;
}
private CheckBox CreateAccessLevelCheckbox()
{
var checkbox = _isMonotone ? new MonotoneCheckBox() : new CheckBox();
checkbox.Margin = new Thickness(0, 0, 0, 3);
checkbox.ToggleMode = true;
checkbox.ReservesSpace = false;
return checkbox;
}
private sealed class AccessLevelEntry : BoxContainer
{
public ProtoId<AccessLevelPrototype> AccessLevel;
public readonly CheckBox CheckBox;
public readonly LineRenderer CheckBoxLink;
public AccessLevelEntry(bool monotone)
{
HorizontalExpand = true;
CheckBoxLink = new LineRenderer
{
SetWidth = 22,
VerticalExpand = true,
Margin = new Thickness(0, -1),
ReservesSpace = false,
};
AddChild(CheckBoxLink);
CheckBox = monotone ? new MonotoneCheckBox() : new CheckBox();
CheckBox.ToggleMode = true;
CheckBox.Margin = new Thickness(0f, 0f, 0f, 3f);
AddChild(CheckBox);
}
public void UpdateCheckBoxLink(List<(Vector2, Vector2)> lines)
{
CheckBoxLink.Lines = lines;
}
}
private sealed class LineRenderer : Control
{
/// <summary>
/// List of lines to render (their start and end x-y coordinates).
/// Position (0,0) is the top left corner of the control and
/// position (1,1) is the bottom right corner.
/// </summary>
/// <remarks>
/// The color of the lines is inherited from the control.
/// </remarks>
public List<(Vector2, Vector2)> Lines;
public LineRenderer()
{
Lines = new List<(Vector2, Vector2)>();
}
public LineRenderer(List<(Vector2, Vector2)> lines)
{
Lines = lines;
}
protected override void Draw(DrawingHandleScreen handle)
{
foreach (var line in Lines)
{
var start = PixelPosition +
new Vector2(PixelWidth * line.Item1.X, PixelHeight * line.Item1.Y);
var end = PixelPosition +
new Vector2(PixelWidth * line.Item2.X, PixelHeight * line.Item2.Y);
handle.DrawLine(start, end, ActualModulateSelf);
}
}
}
}