SwitchButton (#39161)
* Initial toggle switch styling * tweak toggle switch textures * Simplify toggle SVG images a bit * Better name for switch button * Update CheckButtons that were already just regular buttons * Match checkbox/text field outline colour instead of slider outline colour * Use switch button for APC power * Update switch button styling; add separate style for power buttons * Use new switch button in midi channels menu * Add spacer * adjust switch button icon proportions, position * Add disabled toggle switch styles, use improved pressed style setup, make APC breaker state visible to all * Use Janet Blackquill's icon design; remove StyleClassPowerSwitchButton. Co-authored-by: Janet Blackquill <uhhadd@gmail.com> * Style switch children directly instead of with propagated styles * Add attributions file * Turns out source is a required field * Move SwitchButton out of engine * Move styles to sheetlet * Make workaround for child controls not updating work in content * Icon layers * Set up ISwitchButtonConfig * Fix disabled switch label font color * Don't redefine base pseudostyles * Use pseudoclass helpers for better readability * Use margin instead of padding element * Remove unused using statements * Remove extra image file * Update attributions for changed files
@@ -1,5 +1,5 @@
|
||||
<DefaultWindow Title="{Loc 'instruments-component-channels-menu'}" MinSize="250 350" xmlns="https://spacestation14.io"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="true" VerticalExpand="true" Align="Center">
|
||||
<ItemList Name="ChannelList" SelectMode="Multiple" Margin="3 3 3 3" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="8"/>
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="true" VerticalExpand="true" Align="Center"
|
||||
@@ -7,7 +7,8 @@
|
||||
<Button Name="AllButton" Text="{Loc 'instruments-component-channels-all-button'}" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="1"/>
|
||||
<Button Name="ClearButton" Text="{Loc 'instruments-component-channels-clear-button'}" HorizontalExpand="true" VerticalExpand="true" SizeFlagsStretchRatio="1"/>
|
||||
</BoxContainer>
|
||||
<Button Name="DisplayTrackNames"
|
||||
Text="{Loc 'instruments-component-channels-track-names-toggle'}" />
|
||||
<controls:SwitchButton Name="DisplayTrackNames"
|
||||
Text="{Loc 'instruments-component-channels-track-names-toggle'}"
|
||||
Margin="0 5 0 0" />
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
|
||||
@@ -37,7 +37,6 @@ public sealed partial class ChannelsMenu : DefaultWindow
|
||||
|
||||
private void OnDisplayTrackNamesPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
DisplayTrackNames.SetClickPressed(!DisplayTrackNames.Pressed);
|
||||
Populate();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:style="clr-namespace:Content.Client.Stylesheets"
|
||||
Name="APCMenu"
|
||||
Title="{Loc 'apc-menu-title'}"
|
||||
Resizable="False">
|
||||
@@ -19,9 +18,7 @@
|
||||
<!-- Power On/Off -->
|
||||
<Label Text="{Loc 'apc-menu-breaker-label'}" HorizontalExpand="True"
|
||||
StyleClasses="highlight" MinWidth="120"/>
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="150">
|
||||
<Button Name="BreakerButton" Text="{Loc 'apc-menu-breaker-button'}" HorizontalExpand="True" ToggleMode="True"/>
|
||||
</BoxContainer>
|
||||
<controls:SwitchButton Name="BreakerButton" MinWidth="150"/>
|
||||
<!--Charging Status-->
|
||||
<Label Text="{Loc 'apc-menu-external-label'}" StyleClasses="highlight" MinWidth="120" />
|
||||
<Label Name="ExternalPowerStateLabel" Text="{Loc 'apc-menu-power-state-good'}" />
|
||||
|
||||
@@ -33,10 +33,7 @@ namespace Content.Client.Power.APC.UI
|
||||
{
|
||||
var castState = (ApcBoundInterfaceState) state;
|
||||
|
||||
if (!BreakerButton.Disabled)
|
||||
{
|
||||
BreakerButton.Pressed = castState.MainBreaker;
|
||||
}
|
||||
BreakerButton.Pressed = castState.MainBreaker;
|
||||
|
||||
if (PowerLabel != null)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Client.Stylesheets;
|
||||
|
||||
public abstract class CommonStylesheet : PalettedStylesheet, IButtonConfig, IWindowConfig, IIconConfig, ITabContainerConfig,
|
||||
ISliderConfig, IRadialMenuConfig, IPlaceholderConfig, ITooltipConfig, IPanelConfig, INanoHeadingConfig,
|
||||
ILineEditConfig, IStripebackConfig, ICheckboxConfig
|
||||
ILineEditConfig, IStripebackConfig, ICheckboxConfig, ISwitchButtonConfig
|
||||
{
|
||||
/// <remarks>
|
||||
/// This constructor will not access any virtual or abstract properties, so you can set them from your config.
|
||||
@@ -73,4 +73,11 @@ public abstract class CommonStylesheet : PalettedStylesheet, IButtonConfig, IWin
|
||||
ColorPalette IButtonConfig.ButtonPalette => PrimaryPalette with { PressedElement = PositivePalette.PressedElement };
|
||||
ColorPalette IButtonConfig.PositiveButtonPalette => PositivePalette;
|
||||
ColorPalette IButtonConfig.NegativeButtonPalette => NegativePalette;
|
||||
|
||||
ResPath ISwitchButtonConfig.SwitchButtonTrackFillPath => new("switchbutton_track_fill.svg.96dpi.png");
|
||||
ResPath ISwitchButtonConfig.SwitchButtonTrackOutlinePath => new("switchbutton_track_outline.svg.96dpi.png");
|
||||
ResPath ISwitchButtonConfig.SwitchButtonThumbFillPath => new("switchbutton_thumb_fill.svg.96dpi.png");
|
||||
ResPath ISwitchButtonConfig.SwitchButtonThumbOutlinePath => new("switchbutton_thumb_outline.svg.96dpi.png");
|
||||
ResPath ISwitchButtonConfig.SwitchButtonSymbolOffPath => new("switchbutton_symbol_off.svg.96dpi.png");
|
||||
ResPath ISwitchButtonConfig.SwitchButtonSymbolOnPath => new("switchbutton_symbol_on.svg.96dpi.png");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Stylesheets.SheetletConfigs;
|
||||
|
||||
public interface ISwitchButtonConfig
|
||||
{
|
||||
public ResPath SwitchButtonTrackFillPath { get; }
|
||||
public ResPath SwitchButtonTrackOutlinePath { get; }
|
||||
public ResPath SwitchButtonThumbFillPath { get; }
|
||||
public ResPath SwitchButtonThumbOutlinePath { get; }
|
||||
public ResPath SwitchButtonSymbolOffPath { get; }
|
||||
public ResPath SwitchButtonSymbolOnPath { get; }
|
||||
}
|
||||
|
||||
125
Content.Client/Stylesheets/Sheetlets/SwitchButtonSheetlet.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Content.Client.Stylesheets.SheetletConfigs;
|
||||
using Content.Client.Stylesheets.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using static Content.Client.Stylesheets.StylesheetHelpers;
|
||||
|
||||
namespace Content.Client.Stylesheets.Sheetlets;
|
||||
|
||||
[CommonSheetlet]
|
||||
public sealed class SwitchButtonSheetlet<T> : Sheetlet<T> where T : PalettedStylesheet, ISwitchButtonConfig
|
||||
{
|
||||
public override StyleRule[] GetRules(T sheet, object config)
|
||||
{
|
||||
ISwitchButtonConfig switchButtonCfg = sheet;
|
||||
|
||||
var trackFillTex = sheet.GetTextureOr(switchButtonCfg.SwitchButtonTrackFillPath, NanotrasenStylesheet.TextureRoot);
|
||||
var trackOutlineTex = sheet.GetTextureOr(switchButtonCfg.SwitchButtonTrackOutlinePath, NanotrasenStylesheet.TextureRoot);
|
||||
var thumbFillTex = sheet.GetTextureOr(switchButtonCfg.SwitchButtonThumbFillPath, NanotrasenStylesheet.TextureRoot);
|
||||
var thumbOutlineTex = sheet.GetTextureOr(switchButtonCfg.SwitchButtonThumbOutlinePath, NanotrasenStylesheet.TextureRoot);
|
||||
var symbolOffTex = sheet.GetTextureOr(switchButtonCfg.SwitchButtonSymbolOffPath, NanotrasenStylesheet.TextureRoot);
|
||||
var symbolOnTex = sheet.GetTextureOr(switchButtonCfg.SwitchButtonSymbolOnPath, NanotrasenStylesheet.TextureRoot);
|
||||
|
||||
return
|
||||
[
|
||||
// SwitchButton
|
||||
E<SwitchButton>().Prop(SwitchButton.StylePropertySeparation, 10),
|
||||
|
||||
E<SwitchButton>()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassTrackFill))
|
||||
.Prop(TextureRect.StylePropertyTexture, trackFillTex)
|
||||
.Modulate(sheet.SecondaryPalette.BackgroundDark),
|
||||
|
||||
E<SwitchButton>()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassTrackOutline))
|
||||
.Prop(TextureRect.StylePropertyTexture, trackOutlineTex)
|
||||
.Modulate(sheet.SecondaryPalette.Text),
|
||||
|
||||
E<SwitchButton>()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassThumbFill))
|
||||
.Prop(TextureRect.StylePropertyTexture, thumbFillTex)
|
||||
.Modulate(sheet.PrimaryPalette.Element)
|
||||
.HorizontalAlignment(Control.HAlignment.Left),
|
||||
|
||||
E<SwitchButton>()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassThumbOutline))
|
||||
.Prop(TextureRect.StylePropertyTexture, thumbOutlineTex)
|
||||
.Modulate(sheet.PrimaryPalette.Text)
|
||||
.HorizontalAlignment(Control.HAlignment.Left),
|
||||
|
||||
E<SwitchButton>()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassSymbol))
|
||||
.Prop(TextureRect.StylePropertyTexture, symbolOffTex)
|
||||
.Modulate(sheet.SecondaryPalette.Text),
|
||||
|
||||
// Pressed styles
|
||||
E<SwitchButton>()
|
||||
.PseudoPressed()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassTrackFill))
|
||||
.Modulate(sheet.PositivePalette.Text),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoPressed()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassSymbol))
|
||||
.Prop(TextureRect.StylePropertyTexture, symbolOnTex)
|
||||
.Modulate(Color.White), // Same color as text, not yet in any of the palettes
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoPressed()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassThumbFill))
|
||||
.HorizontalAlignment(Control.HAlignment.Right),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoPressed()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassThumbOutline))
|
||||
.HorizontalAlignment(Control.HAlignment.Right),
|
||||
|
||||
// Disabled styles
|
||||
E<SwitchButton>()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassTrackFill))
|
||||
.Modulate(sheet.SecondaryPalette.DisabledElement),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassTrackOutline))
|
||||
.Modulate(sheet.SecondaryPalette.DisabledElement),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassThumbFill))
|
||||
.Modulate(sheet.PrimaryPalette.DisabledElement),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassThumbOutline))
|
||||
.Modulate(sheet.PrimaryPalette.TextDark),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassSymbol))
|
||||
.Modulate(sheet.SecondaryPalette.TextDark),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<Label>())
|
||||
.Modulate(sheet.PrimaryPalette.TextDark),
|
||||
|
||||
// Both pressed & disabled styles
|
||||
// Note that some of the pressed-only and disabled-only styles do not conflict
|
||||
// and will also be used
|
||||
E<SwitchButton>()
|
||||
.PseudoPressed()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassTrackFill))
|
||||
.Modulate(sheet.PositivePalette.DisabledElement),
|
||||
|
||||
E<SwitchButton>()
|
||||
.PseudoPressed()
|
||||
.PseudoDisabled()
|
||||
.ParentOf(E<TextureRect>().Class(SwitchButton.StyleClassSymbol))
|
||||
.Modulate(sheet.PositivePalette.Text),
|
||||
];
|
||||
}
|
||||
}
|
||||
317
Content.Client/UserInterface/Controls/SwitchButton.cs
Normal file
@@ -0,0 +1,317 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// A type of toggleable button that a switch icon and a secondary text label both showing the current state
|
||||
/// </summary>
|
||||
[Virtual]
|
||||
public class SwitchButton : ContainerButton
|
||||
{
|
||||
public const string StyleClassTrackFill = "trackFill";
|
||||
public const string StyleClassTrackOutline = "trackOutline";
|
||||
public const string StyleClassThumbFill = "thumbFill";
|
||||
public const string StyleClassThumbOutline = "thumbOutline";
|
||||
public const string StyleClassSymbol = "symbol";
|
||||
|
||||
public const string StylePropertySeparation = "separation";
|
||||
|
||||
private const int DefaultSeparation = 0;
|
||||
|
||||
private int ActualSeparation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (TryGetStyleProperty(StylePropertySeparation, out int separation))
|
||||
{
|
||||
return separation;
|
||||
}
|
||||
|
||||
return SeparationOverride ?? DefaultSeparation;
|
||||
}
|
||||
}
|
||||
|
||||
public int? SeparationOverride { get; set; }
|
||||
public Label Label { get; }
|
||||
public Label OffStateLabel { get; }
|
||||
public Label OnStateLabel { get; }
|
||||
|
||||
// I tried to find a way not to have five textures here, but the other
|
||||
// options were worse.
|
||||
public TextureRect TrackFill { get; }
|
||||
public TextureRect TrackOutline { get; }
|
||||
public TextureRect ThumbFill { get; }
|
||||
public TextureRect ThumbOutline { get; }
|
||||
public TextureRect Symbol { get; }
|
||||
|
||||
public SwitchButton()
|
||||
{
|
||||
ToggleMode = true;
|
||||
|
||||
TrackFill = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassTrackFill },
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
TrackOutline = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassTrackOutline },
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
ThumbFill = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassThumbFill },
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
ThumbOutline = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassThumbOutline },
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
Symbol = new TextureRect
|
||||
{
|
||||
StyleClasses = { StyleClassSymbol },
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
Label = new Label();
|
||||
Label.Visible = false;
|
||||
|
||||
OffStateLabel = new Label();
|
||||
OffStateLabel.Text = Loc.GetString("toggle-switch-default-off-state-label");
|
||||
OffStateLabel.ReservesSpace = true;
|
||||
|
||||
OnStateLabel = new Label();
|
||||
OnStateLabel.Text = Loc.GetString("toggle-switch-default-on-state-label");
|
||||
OnStateLabel.ReservesSpace = true;
|
||||
OnStateLabel.Visible = false;
|
||||
|
||||
Label.HorizontalExpand = true;
|
||||
|
||||
AddChild(Label);
|
||||
AddChild(TrackFill);
|
||||
AddChild(TrackOutline);
|
||||
AddChild(ThumbFill);
|
||||
AddChild(ThumbOutline);
|
||||
AddChild(Symbol);
|
||||
AddChild(OffStateLabel);
|
||||
AddChild(OnStateLabel);
|
||||
}
|
||||
|
||||
protected override void DrawModeChanged()
|
||||
{
|
||||
// Workaround for child controls not being updated automatically.
|
||||
// Remove once https://github.com/space-wizards/RobustToolbox/pull/6264
|
||||
// or similar is merged.
|
||||
var relevantChangeMade = false;
|
||||
|
||||
if (Disabled)
|
||||
{
|
||||
if (!HasStylePseudoClass(StylePseudoClassDisabled))
|
||||
{
|
||||
AddStylePseudoClass(StylePseudoClassDisabled);
|
||||
relevantChangeMade = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasStylePseudoClass(StylePseudoClassDisabled))
|
||||
{
|
||||
RemoveStylePseudoClass(StylePseudoClassDisabled);
|
||||
relevantChangeMade = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Pressed)
|
||||
{
|
||||
if (!HasStylePseudoClass(StylePseudoClassPressed))
|
||||
{
|
||||
AddStylePseudoClass(StylePseudoClassPressed);
|
||||
relevantChangeMade = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasStylePseudoClass(StylePseudoClassPressed))
|
||||
{
|
||||
RemoveStylePseudoClass(StylePseudoClassPressed);
|
||||
relevantChangeMade = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (relevantChangeMade)
|
||||
{
|
||||
Label.RemoveStyleClass("dummy");
|
||||
TrackFill.RemoveStyleClass("dummy");
|
||||
TrackOutline.RemoveStyleClass("dummy");
|
||||
ThumbFill.RemoveStyleClass("dummy");
|
||||
ThumbOutline.RemoveStyleClass("dummy");
|
||||
Symbol.RemoveStyleClass("dummy");
|
||||
OffStateLabel.RemoveStyleClass("dummy");
|
||||
OnStateLabel.RemoveStyleClass("dummy");
|
||||
}
|
||||
|
||||
// no base.DrawModeChanged() call - ContainerButton's pseudoclass handling
|
||||
// doesn't support a button being both pressed and disabled
|
||||
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, the button will allow shrinking and clip text of the main
|
||||
/// label to prevent the text from going outside the bounds of the button.
|
||||
/// If false, the minimum size will always fit the contained text.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool ClipText { get => Label.ClipText; set => Label.ClipText = value; }
|
||||
|
||||
/// <summary>
|
||||
/// The text displayed by the button's main label.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string? Text
|
||||
{
|
||||
get => Label.Text;
|
||||
set
|
||||
{
|
||||
Label.Text = value;
|
||||
Label.Visible = !string.IsNullOrEmpty(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text displayed by the button's secondary label in the off state.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string? OffStateText
|
||||
{
|
||||
get => OffStateLabel.Text;
|
||||
set => OffStateLabel.Text = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The text displayed by the button's secondary label in the on state.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string? OnStateText
|
||||
{
|
||||
get => OnStateLabel.Text;
|
||||
set => OnStateLabel.Text = value;
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
if (OffStateLabel is not null)
|
||||
{
|
||||
OffStateLabel.Visible = !Pressed;
|
||||
}
|
||||
|
||||
if (OnStateLabel is not null)
|
||||
{
|
||||
OnStateLabel.Visible = Pressed;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void StylePropertiesChanged()
|
||||
{
|
||||
base.StylePropertiesChanged();
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
var desiredSize = Vector2.Zero;
|
||||
var separation = ActualSeparation;
|
||||
|
||||
// Start with the icon, since it always appears
|
||||
if (TrackOutline is not null)
|
||||
{
|
||||
TrackOutline.Measure(availableSize);
|
||||
desiredSize = TrackOutline.DesiredSize;
|
||||
}
|
||||
|
||||
// Add space for the label if it has text
|
||||
if (! string.IsNullOrEmpty(Label?.Text))
|
||||
{
|
||||
Label.Measure(availableSize);
|
||||
desiredSize.X += separation + Label.DesiredSize.X;
|
||||
desiredSize.Y = float.Max(desiredSize.Y, Label.DesiredSize.Y);
|
||||
}
|
||||
|
||||
// Add space for the state labels if at least one of them has text
|
||||
var stateLabelSpace = Vector2.Zero;
|
||||
if (! string.IsNullOrEmpty(OffStateLabel?.Text))
|
||||
{
|
||||
OffStateLabel.Measure(availableSize);
|
||||
stateLabelSpace = OffStateLabel.DesiredSize;
|
||||
}
|
||||
|
||||
if (! string.IsNullOrEmpty(OnStateLabel?.Text))
|
||||
{
|
||||
OnStateLabel.Measure(availableSize);
|
||||
stateLabelSpace.Y = float.Max(stateLabelSpace.Y, OnStateLabel.DesiredSize.Y);
|
||||
stateLabelSpace.X = float.Max(stateLabelSpace.X, OnStateLabel.DesiredSize.X);
|
||||
}
|
||||
|
||||
if (stateLabelSpace != Vector2.Zero)
|
||||
{
|
||||
desiredSize.X += separation + stateLabelSpace.X;
|
||||
desiredSize.Y = float.Max(desiredSize.Y, stateLabelSpace.Y);
|
||||
}
|
||||
|
||||
return desiredSize;
|
||||
}
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
var separation = ActualSeparation;
|
||||
|
||||
var actualMainLabelWidth = finalSize.X - separation - TrackOutline.DesiredSize.X;
|
||||
float iconPosition = 0;
|
||||
float stateLabelPosition = 0;
|
||||
|
||||
if (string.IsNullOrEmpty(Label?.Text))
|
||||
{
|
||||
stateLabelPosition = TrackOutline.DesiredSize.X + separation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(OffStateLabel?.Text) || !string.IsNullOrEmpty(OnStateLabel?.Text))
|
||||
{
|
||||
var stateLabelsWidth = float.Max(OffStateLabel!.DesiredSize.X, OnStateLabel.DesiredSize.X);
|
||||
actualMainLabelWidth -= (separation + stateLabelsWidth);
|
||||
}
|
||||
actualMainLabelWidth = float.Max(actualMainLabelWidth, 0);
|
||||
iconPosition = actualMainLabelWidth + separation;
|
||||
stateLabelPosition = iconPosition + TrackOutline.DesiredSize.X + separation;
|
||||
}
|
||||
|
||||
var mainLabelTargetBox = new UIBox2(0, 0, actualMainLabelWidth, finalSize.Y);
|
||||
Label?.Arrange(mainLabelTargetBox);
|
||||
|
||||
var iconTargetBox = new UIBox2(iconPosition, 0, iconPosition + TrackOutline.DesiredSize.X, finalSize.Y);
|
||||
TrackFill.Arrange(iconTargetBox);
|
||||
TrackOutline.Arrange(iconTargetBox);
|
||||
Symbol.Arrange(iconTargetBox);
|
||||
|
||||
ThumbOutline.Measure(TrackOutline.DesiredSize); // didn't measure in MeasureOverride, don't need its size there
|
||||
var thumbLeft = iconTargetBox.Left;
|
||||
if (Pressed)
|
||||
thumbLeft = iconTargetBox.Right - ThumbOutline.DesiredSize.X;
|
||||
var thumbTargetBox = new UIBox2(thumbLeft, 0, thumbLeft + ThumbOutline.DesiredSize.X, finalSize.Y);
|
||||
ThumbFill.Arrange(thumbTargetBox);
|
||||
ThumbOutline.Arrange(thumbTargetBox);
|
||||
|
||||
var stateLabelsTargetBox = new UIBox2(stateLabelPosition, 0, finalSize.X, finalSize.Y);
|
||||
OffStateLabel?.Arrange(stateLabelsTargetBox);
|
||||
OnStateLabel?.Arrange(stateLabelsTargetBox);
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
## Loc strings for generic "on/off button" control.
|
||||
ui-button-off = Off
|
||||
ui-button-on = On
|
||||
|
||||
# These are for switch labels that indicate the current state
|
||||
toggle-switch-default-off-state-label = Off
|
||||
toggle-switch-default-on-state-label = On
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
apc-menu-title = APC
|
||||
apc-menu-breaker-label = Main Breaker
|
||||
apc-menu-breaker-button = Toggle
|
||||
apc-menu-power-label = Load
|
||||
apc-menu-external-label = External Power
|
||||
apc-menu-charge-label = {$percent} Charged
|
||||
|
||||
9
Resources/Textures/Interface/Nano/attributions.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
- files: ["switchbutton_symbol_off.svg",
|
||||
"switchbutton_symbol_on.svg",
|
||||
"switchbutton_thumb_fill.svg",
|
||||
"switchbutton_thumb_outline.svg",
|
||||
"switchbutton_track_fill.svg",
|
||||
"switchbutton_track_outline.svg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Design by Janet Blackquill <uhhadd@gmail.com>"
|
||||
source: "https://github.com/sowelipililimute"
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="48"
|
||||
height="26"
|
||||
viewBox="0 0 48 26"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="switchbutton_label_off.svg"
|
||||
inkscape:export-filename="switchbutton_symbol_off.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#7f7f7f"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="false"
|
||||
inkscape:zoom="12.618721"
|
||||
inkscape:cx="19.138231"
|
||||
inkscape:cy="15.215488"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="828"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"><inkscape:grid
|
||||
id="grid2"
|
||||
units="px"
|
||||
originx="2.220446e-16"
|
||||
originy="0"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><circle
|
||||
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path4"
|
||||
cx="34"
|
||||
cy="13"
|
||||
r="5" /></g></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 387 B |
60
Resources/Textures/Interface/Nano/switchbutton_symbol_on.svg
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="48"
|
||||
height="26"
|
||||
viewBox="0 0 48 26"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="switchbutton_symbol_on.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
inkscape:export-filename="switchbutton_symbol_on.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#7f7f7f"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:zoom="6.3093604"
|
||||
inkscape:cx="14.977746"
|
||||
inkscape:cy="-1.7434414"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="828"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="1.1581421e-08"
|
||||
originy="0.0059233303"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,0.00592333)"><path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers;fill-opacity:1"
|
||||
d="M 15,6.9999999 V 19"
|
||||
id="path1" /></g></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 192 B |
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="24"
|
||||
height="26.000452"
|
||||
viewBox="0 0 24 26.000452"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="switchbutton_thumb_fill.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
inkscape:export-filename="switchbutton_thumb_fill.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#7f7f7f"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:zoom="6.3093604"
|
||||
inkscape:cx="14.977746"
|
||||
inkscape:cy="10.936132"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="828"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="1.1581421e-08"
|
||||
originy="3.8550387e-06"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 1,1 V 15.576073 L 10.419922,24.99408 23,24.99998 V 10.419969 L 13.582031,1.0000485 Z"
|
||||
id="path3-7-0"
|
||||
sodipodi:nodetypes="ccccccc" /></g></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 256 B |
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="24"
|
||||
height="26.000452"
|
||||
viewBox="0 0 24 26.000453"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="switchbutton_thumb_outline.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
inkscape:export-filename="switchbutton_thumb_outline.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#7f7f7f"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:zoom="12.618721"
|
||||
inkscape:cx="14.779628"
|
||||
inkscape:cy="12.124842"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="828"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="-24"
|
||||
originy="3.8764731e-06"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-24.000001)"><path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 25.000001,0.99999998 V 15.576073 l 9.419922,9.418007 12.580078,0.0059 V 10.419969 L 37.582032,1.0000485 Z"
|
||||
id="path3-7"
|
||||
sodipodi:nodetypes="ccccccc" /></g></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 269 B |
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="48"
|
||||
height="26.000023"
|
||||
viewBox="0 0 48 26.000023"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="switchbutton_track_fill.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
inkscape:export-filename="switchbutton_track_fill.svg.96dpi.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#7f7f7f"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:zoom="6.3093604"
|
||||
inkscape:cx="14.977746"
|
||||
inkscape:cy="10.936132"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="828"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="1.1581421e-08"
|
||||
originy="0.0059233341"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,0.00592333)"><path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 1,1.0000182 V 15.576092 L 10.419922,24.9941 47,24.994096 l 10e-7,-14.580032 -9.417969,-9.4199208 z"
|
||||
id="path3-7"
|
||||
sodipodi:nodetypes="ccccccc" /></g></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 283 B |
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="48"
|
||||
height="26.000023"
|
||||
viewBox="0 0 48 26.000024"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="switchbutton_track_outline.svg"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
inkscape:export-filename="switchbutton_track_outline.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#7f7f7f"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showguides="true"
|
||||
inkscape:zoom="8"
|
||||
inkscape:cx="15.3125"
|
||||
inkscape:cy="34.25"
|
||||
inkscape:window-width="1600"
|
||||
inkscape:window-height="828"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"><inkscape:grid
|
||||
id="grid1"
|
||||
units="px"
|
||||
originx="1.1581421e-08"
|
||||
originy="0.0059415338"
|
||||
spacingx="1"
|
||||
spacingy="1"
|
||||
empcolor="#0099e5"
|
||||
empopacity="0.30196078"
|
||||
color="#0099e5"
|
||||
opacity="0.14901961"
|
||||
empspacing="5"
|
||||
enabled="true"
|
||||
visible="true" /></sodipodi:namedview><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,0.00594153)"><path
|
||||
style="baseline-shift:baseline;display:inline;overflow:visible;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;enable-background:accumulate;stop-color:#000000"
|
||||
d="M 1,0.99999997 V 15.576073 l 9.419922,9.418008 36.580078,-4e-6 10e-7,-14.580031 -9.417969,-9.419921 z"
|
||||
id="path3-7"
|
||||
sodipodi:nodetypes="ccccccc" /></g></svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 331 B |