├── Images ├── Screenshots │ ├── Links.png │ ├── HybridTheme.png │ ├── PlanetsDark.png │ ├── PlanetsLight.png │ └── TextEditorJsonAndXml.png ├── Logo │ └── png │ │ ├── SideScroll_128.png │ │ ├── SideScroll_16.png │ │ ├── SideScroll_24.png │ │ ├── SideScroll_256.png │ │ ├── SideScroll_32.png │ │ ├── SideScroll_40.png │ │ ├── SideScroll_48.png │ │ └── SideScroll_64.png └── Animations │ └── SideScroll-VideoGamesDB.gif ├── Programs ├── SideScroll.Demo.Avalonia.Browser │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ └── launchSettings.json │ ├── wwwroot │ │ ├── favicon.ico │ │ ├── main.js │ │ ├── index.html │ │ └── app.css │ ├── runtimeconfig.template.json │ ├── Program.cs │ └── SideScroll.Demo.Avalonia.Browser.csproj ├── SideScroll.Demo.Console │ ├── SideScroll.Demo.Console.csproj │ └── Console.cs └── SideScroll.Demo.Avalonia.Desktop │ ├── SideScroll.Demo.Avalonia.Desktop.csproj │ └── Program.cs ├── .gitattributes ├── Libraries ├── SideScroll.Resources │ ├── Samples │ │ └── Text │ │ │ └── SolarSystem.txt │ ├── Icons │ │ └── svg │ │ │ ├── originals │ │ │ ├── Minimize.svg │ │ │ ├── Undo.svg │ │ │ ├── Download.svg │ │ │ ├── Copy.svg │ │ │ ├── Info.svg │ │ │ ├── Maximize.svg │ │ │ ├── RightArrow.svg │ │ │ ├── UpArrow.svg │ │ │ ├── DownArrow.svg │ │ │ ├── Bookmark.svg │ │ │ ├── Restore.svg │ │ │ ├── Search.svg │ │ │ ├── Duplicate.svg │ │ │ ├── LeftArrow.svg │ │ │ ├── LeftArrowCircle.svg │ │ │ ├── Add.svg │ │ │ ├── PlayCircle.svg │ │ │ ├── Reset.svg │ │ │ ├── RightArrowCircle.svg │ │ │ ├── Cancel.svg │ │ │ ├── Star.svg │ │ │ ├── Close.svg │ │ │ ├── PanelLeftContract.svg │ │ │ ├── Refresh.svg │ │ │ ├── Clip.svg │ │ │ ├── Shutter.svg │ │ │ ├── Placeholder.svg │ │ │ ├── PadNote.svg │ │ │ ├── PanelLeftExpand.svg │ │ │ ├── Stats.svg │ │ │ ├── BlankDocument.svg │ │ │ ├── Link.svg │ │ │ ├── Eraser.svg │ │ │ ├── List1.svg │ │ │ ├── Report.svg │ │ │ ├── Enter.svg │ │ │ ├── Import.svg │ │ │ ├── StopCircle.svg │ │ │ └── OpenFolder.svg │ │ │ ├── Download.svg │ │ │ ├── Undo.svg │ │ │ ├── Redo.svg │ │ │ ├── Copy.svg │ │ │ ├── LeftArrow.svg │ │ │ ├── DownArrow.svg │ │ │ ├── Reset.svg │ │ │ └── Minimize.svg │ ├── Logo.cs │ ├── TextSamples.cs │ └── ResourceView.cs ├── SideScroll.Avalonia │ ├── Controls │ │ ├── TabTextBlock.cs │ │ ├── TabMenuItem.cs │ │ ├── TabButton.cs │ │ ├── Flyouts │ │ │ ├── MessageFlyout.cs │ │ │ └── ConfirmationFlyout.cs │ │ ├── TabCheckBox.cs │ │ ├── ScreenCapture │ │ │ └── ScreenCaptureToolbar.cs │ │ ├── Toolbar │ │ │ └── ToolbarTextBox.cs │ │ ├── TabTextArea.cs │ │ └── BaseView.cs │ ├── Assets │ │ └── Fonts │ │ │ └── SourceCodePro-Regular.ttf │ ├── Tabs │ │ ├── TabObjectEditable.cs │ │ ├── TabTextFile.cs │ │ └── TabText.cs │ ├── Themes │ │ ├── Controls │ │ │ ├── TabTextBlock.xaml │ │ │ ├── TabForm.xaml │ │ │ ├── TabViewer.xaml │ │ │ ├── TabSeparator.xaml │ │ │ ├── TabAvaloniaEdit.xaml │ │ │ ├── TabSplitGrid.xaml │ │ │ ├── TabHeader.xaml │ │ │ ├── TabTextArea.xaml │ │ │ └── TabSplitter.xaml │ │ └── ImageColorView.cs │ ├── Samples │ │ └── Controls │ │ │ ├── TabSampleTextArea.cs │ │ │ └── TabSampleControls.cs │ ├── Extensions │ │ └── AvaloniaExtensions.cs │ ├── AvaloniaAssets.cs │ └── Utilities │ │ └── ImageUtils.cs ├── SideScroll.Avalonia.Samples │ ├── Assets │ │ ├── Logo │ │ │ └── SideScroll.ico │ │ └── Icons.cs │ ├── SampleUserSettings.cs │ ├── Charts │ │ ├── TabCustomCharts.cs │ │ └── TabChartSizes.cs │ ├── SampleProjectSettings.cs │ ├── MainView.cs │ ├── MainWindow.cs │ ├── App.xaml.cs │ ├── Tabs │ │ └── TabAvalonia.cs │ ├── SideScroll.Avalonia.Samples.csproj │ └── App.xaml ├── SideScroll.Tabs │ ├── Toolbar │ │ ├── TabToolbar.cs │ │ └── ToolComboBox.cs │ ├── Settings │ │ └── WindowSettings.cs │ ├── Bookmarks │ │ ├── LinkManager.cs │ │ └── TabLinks.cs │ ├── Lists │ │ ├── ListItemTemplate.cs │ │ ├── ListPairObject.cs │ │ ├── ListByte.cs │ │ └── ListToString.cs │ ├── SideScroll.Tabs.csproj │ ├── TabUtils.cs │ └── TabInterfaces.cs ├── SideScroll │ ├── SideScrollInit.cs │ ├── Interfaces.cs │ ├── BaseTest.cs │ ├── Utilities │ │ ├── ThreadUtils.cs │ │ └── LogUtils.cs │ ├── Logs │ │ ├── LogWriterConsole.cs │ │ └── LogTimer.cs │ ├── Tasks │ │ ├── TaskAction.cs │ │ ├── TaskMethod.cs │ │ └── TaskInstanceCollection.cs │ ├── SideScroll.csproj │ └── CallTimer.cs ├── SideScroll.Tabs.Samples │ ├── Objects │ │ ├── TabSampleDebug.cs │ │ ├── TabSampleJson.cs │ │ ├── TabSampleSubClassProperty.cs │ │ ├── TabSampleObjects.cs │ │ ├── TabSampleObjectMembers.cs │ │ └── TabSampleObjectProperties.cs │ ├── Exceptions │ │ ├── TabSampleLoadException.cs │ │ └── TabSampleExceptions.cs │ ├── Forms │ │ ├── TabSampleForms.cs │ │ ├── TabSampleItem.cs │ │ ├── TabSampleFormUpdating.cs │ │ └── Todo │ │ │ └── SampleTodoItem.cs │ ├── Loading │ │ ├── TabSampleLoadModel.cs │ │ ├── TabSampleLoading.cs │ │ ├── TabSampleLoadAsyncItem.cs │ │ ├── TabSampleSkip.cs │ │ ├── TabSampleLoadItemProperties.cs │ │ ├── TabSampleLoadAsyncItemDelegate.cs │ │ └── TabSampleLoadAsync.cs │ ├── DataGrid │ │ ├── TabSampleGridEditing.cs │ │ ├── TabSampleArray.cs │ │ ├── TabSampleBytes.cs │ │ ├── TabSampleFilter.cs │ │ ├── TabSampleNullableArray.cs │ │ ├── TabSampleGridHashSet.cs │ │ ├── TabSampleInstanceListItems.cs │ │ ├── TabSampleEnums.cs │ │ ├── TabSampleGridDictionary.cs │ │ ├── TabSampleGridColumnOrdering.cs │ │ ├── TabSampleHeaders.cs │ │ ├── TabSampleDataGrid.cs │ │ └── TabSampleGridDataTable.cs │ ├── TabIcons.cs │ ├── Charts │ │ ├── TabSampleChartNoData.cs │ │ ├── TabSampleCharts.cs │ │ └── TabSampleChartSeriesCount.cs │ ├── TabSampleProcess.cs │ ├── TabSampleBookmarks.cs │ ├── SideScroll.Tabs.Samples.csproj │ ├── Demo │ │ └── TabSampleDemo.cs │ ├── TabSamples.cs │ └── DataRepo │ │ └── TabSampleDataRepo.cs ├── SideScroll.Tabs.Tools │ ├── FileViewer │ │ ├── FileDataRepos.cs │ │ ├── TabFileBytes.cs │ │ ├── TabDrives.cs │ │ └── TabFileViewer.cs │ ├── TabTools.cs │ ├── SideScroll.Tabs.Tools.csproj │ └── TabUserSettings.cs ├── SideScroll.Avalonia.Charts.LiveCharts │ ├── LiveChartExtensions.cs │ ├── Themes │ │ ├── ControlThemes.xaml │ │ └── TabLiveChart.xaml │ ├── TabLiveChartLegend.cs │ ├── SideScroll.Avalonia.Charts.LiveCharts.csproj │ └── TabLiveChartLegendItem.cs ├── SideScroll.Network │ ├── Http │ │ ├── HttpFile.cs │ │ ├── HttpCachedCall.cs │ │ ├── HttpCacheManager.cs │ │ └── HttpClientManager.cs │ └── SideScroll.Network.csproj ├── SideScroll.Avalonia.Charts │ └── SideScroll.Avalonia.Charts.csproj └── SideScroll.Serialize │ ├── Atlas │ └── TypeRepos │ │ ├── TypeRepoDecimal.cs │ │ ├── TypeRepoString.cs │ │ ├── TypeRepoUnknown.cs │ │ └── TypeRepoVersion.cs │ └── SideScroll.Serialize.csproj ├── .gitignore ├── NuGet.Config ├── Tests ├── SideScroll.Serialize.Tests │ ├── SerializeBaseTest.cs │ ├── SerializeJsonTests.cs │ ├── DeepCloneTests.cs │ ├── SideScroll.Serialize.Tests.csproj │ └── SerializeItemCollectionTests.cs ├── SideScroll.Tests │ ├── FileUtilsTests.cs │ ├── JsonTests.cs │ ├── CoreTests.cs │ ├── SideScroll.Tests.csproj │ ├── CallTests.cs │ ├── FormatTests.cs │ └── ObjectUtilsTests.cs ├── SideScroll.Tabs.Tests │ └── SideScroll.Tabs.Tests.csproj ├── SideScroll.Avalonia.Tests │ ├── SideScroll.Avalonia.Tests.csproj │ ├── AvaloniaFieldValueConverterTests.cs │ └── AvaloniaDataBindingTests.cs └── SideScroll.Network.Tests │ └── SideScroll.Network.Tests.csproj ├── Docs └── Dev │ ├── Building.md │ ├── ProjectSetup.md │ ├── Logs.md │ ├── Development.md │ └── IDEs │ └── VisualStudioCode.md ├── .vscode ├── tasks.json └── launch.json ├── .github └── workflows │ └── dotnet.yml ├── LICENSE └── SideScroll.Desktop.slnf /Images/Screenshots/Links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Screenshots/Links.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_128.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_16.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_24.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_256.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_32.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_40.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_48.png -------------------------------------------------------------------------------- /Images/Logo/png/SideScroll_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Logo/png/SideScroll_64.png -------------------------------------------------------------------------------- /Images/Screenshots/HybridTheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Screenshots/HybridTheme.png -------------------------------------------------------------------------------- /Images/Screenshots/PlanetsDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Screenshots/PlanetsDark.png -------------------------------------------------------------------------------- /Images/Screenshots/PlanetsLight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Screenshots/PlanetsLight.png -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Runtime.Versioning.SupportedOSPlatform("browser")] 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Convert to native line feeds on checkout to avoid git warnings 2 | * text=auto 3 | *.png -text 4 | *.ico -text 5 | *.fla -text 6 | -------------------------------------------------------------------------------- /Images/Screenshots/TextEditorJsonAndXml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Screenshots/TextEditorJsonAndXml.png -------------------------------------------------------------------------------- /Images/Animations/SideScroll-VideoGamesDB.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Images/Animations/SideScroll-VideoGamesDB.gif -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Samples/Text/SolarSystem.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Libraries/SideScroll.Resources/Samples/Text/SolarSystem.txt -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/TabTextBlock.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SideScroll.Avalonia.Controls; 4 | 5 | public class TabTextBlock : TextBlock; 6 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Programs/SideScroll.Demo.Avalonia.Browser/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/Assets/Logo/SideScroll.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Libraries/SideScroll.Avalonia.Samples/Assets/Logo/SideScroll.ico -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Toolbar/TabToolbar.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Toolbar; 2 | 3 | public class TabToolbar 4 | { 5 | public List Buttons { get; set; } = []; 6 | } 7 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Assets/Fonts/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SideScrollUI/SideScroll/HEAD/Libraries/SideScroll.Avalonia/Assets/Fonts/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.cache 2 | *.diff 3 | *.dll 4 | *.exe 5 | *.nupkg 6 | *.pdb 7 | *.psess 8 | *.suo 9 | *.tmp 10 | *.user 11 | *.vspx 12 | *.zip 13 | *.DS_Store 14 | .idea/ 15 | .vs/ 16 | bin/ 17 | obj/ 18 | packages/ 19 | qodana.yaml -------------------------------------------------------------------------------- /Libraries/SideScroll/SideScrollInit.cs: -------------------------------------------------------------------------------- 1 | 2 | using SideScroll.Utilities; 3 | 4 | namespace SideScroll; 5 | 6 | public static class SideScrollInit 7 | { 8 | public static void Initialize() 9 | { 10 | FileUtils.SetUmaskUserOnly(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/runtimeconfig.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasmHostProperties": { 3 | "perHostConfig": [ 4 | { 5 | "name": "browser", 6 | "host": "browser" 7 | } 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /Libraries/SideScroll/Interfaces.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll; 2 | 3 | // TabViewer will show loaded object instead 4 | public interface ILoadAsync 5 | { 6 | Task LoadAsync(Call call); 7 | } 8 | 9 | // Called when viewing a link 10 | public interface IReload 11 | { 12 | void Reload(); 13 | } 14 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Objects/TabSampleDebug.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Samples.Objects; 4 | 5 | [DebugOnly] 6 | public class TabSampleDebug 7 | { 8 | public static string Release => "Release"; 9 | 10 | [DebugOnly] 11 | public static string Debug => "Debug"; 12 | } 13 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/FileViewer/FileDataRepos.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Tools.FileViewer; 2 | 3 | public static class FileDataRepos 4 | { 5 | public static FileNodeDataRepoView Favorites { get; set; } = new("Favorites"); 6 | public static FileNodeDataRepoView Recent { get; set; } = new("Recent", true, 30); 7 | } 8 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Minimize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Undo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Settings/WindowSettings.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Settings; 2 | 3 | public class WindowSettings 4 | { 5 | public bool Maximized { get; set; } 6 | 7 | public double Left { get; set; } 8 | public double Top { get; set; } 9 | 10 | public double Width { get; set; } = 1600; 11 | public double Height { get; set; } = 1050; 12 | } 13 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts.LiveCharts/LiveChartExtensions.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | using SkiaSharp; 3 | 4 | namespace SideScroll.Avalonia.Charts.LiveCharts; 5 | 6 | public static class LiveChartExtensions 7 | { 8 | public static SKColor AsSkColor(this Color color) 9 | { 10 | return new SKColor(color.R, color.G, color.B, color.A); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/SampleUserSettings.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Settings; 3 | 4 | namespace SideScroll.Avalonia.Samples; 5 | 6 | public class SampleUserSettings : UserSettings 7 | { 8 | [Header("Custom"), WordWrap] 9 | public string ApiUri { get; set; } = @"http://localhost:80/"; 10 | 11 | public int CustomLimit { get; set; } = 42; 12 | } 13 | -------------------------------------------------------------------------------- /Libraries/SideScroll/BaseTest.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Logs; 2 | 3 | namespace SideScroll; 4 | 5 | public class BaseTest 6 | { 7 | protected Call Call { get; set; } = new(); 8 | 9 | public virtual void Initialize(string name, LogLevel logLevel = LogLevel.Info) 10 | { 11 | Call = new Call(name); 12 | Call.Log.Settings!.DebugPrintLogLevel = logLevel; 13 | new LogWriterConsole(Call.Log); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/TabMenuItem.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace SideScroll.Avalonia.Controls; 4 | 5 | public class TabMenuItem : MenuItem 6 | { 7 | protected override Type StyleKeyOverride => typeof(MenuItem); 8 | 9 | public TabMenuItem(string? header = null) 10 | { 11 | Header = header; 12 | ItemsSource = null; // Clear to avoid weak event handler leaks 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Exceptions/TabSampleLoadException.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.Exceptions; 2 | 3 | public class TabSampleLoadException : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | public override void Load(Call call, TabModel model) 10 | { 11 | throw new Exception("Load exception"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts.LiveCharts/Themes/ControlThemes.xaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/SideScroll.Serialize.Tests/SerializeBaseTest.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Logs; 2 | 3 | namespace SideScroll.Serialize.Tests; 4 | 5 | public class SerializeBaseTest : BaseTest 6 | { 7 | protected static string TestPath => Environment.CurrentDirectory; 8 | 9 | // Log level can be raised for performance testing 10 | protected new void Initialize(string name, LogLevel logLevel = LogLevel.Info) 11 | { 12 | base.Initialize(name, logLevel); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/FileUtilsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Utilities; 3 | 4 | namespace SideScroll.Tests; 5 | 6 | [Category("Core")] 7 | public class FileUtilsTests : BaseTest 8 | { 9 | [OneTimeSetUp] 10 | public void BaseSetup() 11 | { 12 | Initialize("FileUtils"); 13 | } 14 | 15 | [Test] 16 | public void IsFileOpenFileNotFound() 17 | { 18 | Assert.That(FileUtils.IsFileOpen("not_a_file"), Is.False); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/Download.svg: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/Charts/TabCustomCharts.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Samples.Charts; 3 | 4 | namespace SideScroll.Avalonia.Samples.Charts; 5 | 6 | [ListItem] 7 | public class TabCustomCharts 8 | { 9 | public static TabDashboard Dashboard => new(); 10 | public static TabChartSizes Sizes => new(); 11 | public static TabChartUpdating Updating => new(); 12 | public static TabSampleCharts Samples => new(); 13 | } 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/Assets/Icons.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Resources; 2 | using System.Reflection; 3 | 4 | namespace SideScroll.Avalonia.Samples.Assets; 5 | 6 | public static class Icons 7 | { 8 | public const string IconPath = "SideScroll.Avalonia.Samples.Assets"; 9 | 10 | public static Assembly Assembly => Assembly.GetExecutingAssembly(); 11 | 12 | public static ResourceView SideScroll => new(Assembly, IconPath, "Logo", "SideScroll", "ico"); 13 | } 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Download.svg: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Forms/TabSampleForms.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Samples.Forms.Todo; 3 | 4 | namespace SideScroll.Tabs.Samples.Forms; 5 | 6 | [ListItem] 7 | public class TabSampleForms 8 | { 9 | public static TabSampleFormDataTabs DataTabs => new(); 10 | public static TabSampleFormTasks Tasks => new(); 11 | public static TabSampleFormUpdating Updating => new(); 12 | public static TabSampleTodos Todo => new(); 13 | } 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Bookmarks/LinkManager.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Bookmarks; 2 | 3 | public class LinkManager(Project project) 4 | { 5 | public static LinkManager? Instance { get; set; } 6 | 7 | public LinkCollection Created { get; } = new(project, "Created"); 8 | public LinkCollection Imported { get; } = new(project, "Imported"); 9 | 10 | public void Reload(Call call) 11 | { 12 | Created.Load(call, true); 13 | Imported.Load(call, true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Browser; 3 | using SideScroll.Avalonia.Samples; 4 | 5 | namespace SideScroll.Demo.Avalonia.Browser; 6 | 7 | internal sealed class Program 8 | { 9 | private static Task Main(string[] args) => BuildAvaloniaApp() 10 | .WithInterFont() 11 | .StartBrowserAppAsync("out"); 12 | 13 | private static AppBuilder BuildAvaloniaApp() 14 | => AppBuilder.Configure(); 15 | } 16 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Network/Http/HttpFile.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Network.Http; 2 | 3 | public class HttpFile 4 | { 5 | public Uri? Uri { get; set; } 6 | public int? Size { get; set; } 7 | 8 | public override string? ToString() => Uri?.Query; 9 | 10 | public async Task DownloadAsync(Call call, HttpCache httpCache) 11 | { 12 | var cachedHttp = new HttpCachedCall(call, httpCache); 13 | byte[] bytes = await cachedHttp.GetBytesAsync(Uri!.ToString()); 14 | Size = bytes.Length; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Info.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Maximize.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Docs/Dev/Building.md: -------------------------------------------------------------------------------- 1 | # Building 2 | 3 | ## Clone 4 | 5 | - `git clone https://github.com/SideScrollUI/SideScroll.git` 6 | - `cd SideScroll` 7 | 8 | ## Build 9 | 10 | - `dotnet build` 11 | 12 | ## Run 13 | 14 | - `dotnet run --project Programs/SideScroll.Demo.Avalonia.Desktop/SideScroll.Demo.Avalonia.Desktop.csproj` 15 | 16 | ### Pack 17 | 18 | - Update `` and `` in [Directory.Build.props](../../Directory.Build.props) 19 | - `git tag v0.3.0` 20 | - `dotnet pack -o Packages` 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/SampleProjectSettings.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Resources; 2 | using SideScroll.Tabs.Settings; 3 | 4 | namespace SideScroll.Avalonia.Samples; 5 | 6 | public static class SampleProjectSettings 7 | { 8 | public static ProjectSettings Settings => new() 9 | { 10 | Name = "SideScroll", 11 | LinkType = "sidescroll", 12 | Version = ProjectSettings.ProgramVersion(), 13 | DataVersion = new Version(0, 14, 0), 14 | CustomTitleIcon = Logo.Svg.SideScrollTranslucent, 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/MainView.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Charts.LiveCharts; 2 | using SideScroll.Avalonia.Controls; 3 | using SideScroll.Avalonia.Samples.Tabs; 4 | using SideScroll.Tabs; 5 | 6 | namespace SideScroll.Avalonia.Samples; 7 | 8 | public class MainView : BaseView 9 | { 10 | public MainView() : base(Project.Load(SampleProjectSettings.Settings)) 11 | { 12 | LoadTab(new TabAvalonia()); 13 | 14 | LiveChartCreator.Register(); 15 | TabViewer.Toolbar?.AddRightControls(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/TabButton.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Data; 3 | 4 | namespace SideScroll.Avalonia.Controls; 5 | 6 | public class TabButton : Button 7 | { 8 | public TabButton(string? label = null) 9 | { 10 | Content = label; 11 | } 12 | 13 | public void BindVisible(string propertyName) 14 | { 15 | var binding = new Binding(propertyName) 16 | { 17 | Path = propertyName, 18 | Mode = BindingMode.OneWay, 19 | }; 20 | Bind(IsVisibleProperty, binding); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleLoadModel.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.Loading; 2 | 3 | public class TabSampleLoadModel : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | public Instance() 10 | { 11 | LoadingMessage = "Loading ALL the things!"; 12 | } 13 | 14 | public override void Load(Call call, TabModel model) 15 | { 16 | Thread.Sleep(5000); 17 | 18 | model.AddObject("Finished"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/wwwroot/main.js: -------------------------------------------------------------------------------- 1 | import { dotnet } from './_framework/dotnet.js' 2 | 3 | const is_browser = typeof window != "undefined"; 4 | if (!is_browser) throw new Error(`Expected to be running in a browser`); 5 | 6 | const dotnetRuntime = await dotnet 7 | .withDiagnosticTracing(false) 8 | .withApplicationArgumentsFromQuery() 9 | .create(); 10 | 11 | const config = dotnetRuntime.getConfig(); 12 | 13 | await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]); 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Tabs/TabObjectEditable.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs; 2 | 3 | namespace SideScroll.Avalonia.Tabs; 4 | 5 | public class TabObjectEditable(object obj) : ITab 6 | { 7 | public object Object => obj; 8 | 9 | public TabInstance Create() => new Instance(this); 10 | 11 | private class Instance(TabObjectEditable tab) : TabInstance 12 | { 13 | public override void LoadUI(Call call, TabModel model) 14 | { 15 | model.MinDesiredWidth = 225; 16 | 17 | model.AddForm(tab.Object, true); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabTextBlock.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/JsonTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Utilities; 3 | 4 | namespace SideScroll.Tests; 5 | 6 | [Category("Json")] 7 | public class JsonTests : BaseTest 8 | { 9 | [OneTimeSetUp] 10 | public void BaseSetup() 11 | { 12 | Initialize("Json"); 13 | } 14 | 15 | [Test] 16 | public void UnencodedPlus() 17 | { 18 | string input = "{\"name\": \"+\"}"; 19 | Assert.That(JsonUtils.TryFormat(input, out string? formatted)); 20 | 21 | Assert.That(formatted!.Contains("u002B"), Is.False); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Objects/TabSampleJson.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Resources; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Samples.Objects; 5 | 6 | public class TabSampleJson : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | public override void Load(Call call, TabModel model) 13 | { 14 | model.Items = new List 15 | { 16 | new("Sample Text", LazyJsonNode.Parse(TextSamples.Json)), 17 | }; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SideScroll.Demo.Avalonia.Browser": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "applicationUrl": "https://localhost:7169;http://localhost:5235", 10 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/FileViewer/TabFileBytes.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Tools.FileViewer; 5 | 6 | [PrivateData] 7 | public class TabFileBytes(string path) : ITab 8 | { 9 | public string Path => path; 10 | 11 | public TabInstance Create() => new Instance(this); 12 | 13 | private class Instance(TabFileBytes tab) : TabInstance 14 | { 15 | public override void Load(Call call, TabModel model) 16 | { 17 | model.Items = ListByte.Load(tab.Path); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/CoreTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Extensions; 3 | 4 | namespace SideScroll.Tests; 5 | 6 | [Category("Core")] 7 | public class CoreTests : BaseTest 8 | { 9 | [OneTimeSetUp] 10 | public void BaseSetup() 11 | { 12 | Initialize("Core"); 13 | } 14 | 15 | [Test, Description("DecimalToString")] 16 | [Ignore("todo: fix")] 17 | public void DecimalToString() 18 | { 19 | decimal d = 123456.1234M; 20 | string text = d.Formatted()!; 21 | 22 | Assert.That(text, Is.EqualTo("123,456.1234")); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/RightArrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/UpArrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/SideScroll.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/DownArrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Exceptions/TabSampleExceptions.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Samples.Exceptions; 4 | 5 | public class TabSampleExceptions : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | public override void Load(Call call, TabModel model) 12 | { 13 | model.Items = new List 14 | { 15 | new("Load Exception", new TabSampleLoadException()), 16 | }; 17 | 18 | call.Log.AddError("Load error"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/SideScroll.Serialize.Tests/SerializeJsonTests.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Serialize.Tests; 2 | 3 | public class SerializeJsonTests 4 | { 5 | /*[Test, Description("Serialize Lazy Base")] 6 | public void SerializeJsonBase() 7 | { 8 | var input = new Parent(); 9 | input.child = new Child(); 10 | input.child.uintTest = 2; 11 | 12 | var serializer = new SerializerMemoryJson( 13 | serializerFile.Save(Call, input); 14 | Parent output = serializerFile.Load(Call, true); 15 | 16 | Assert.AreEqual(input.child.uintTest, output.child.uintTest); 17 | }*/ 18 | } 19 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabForm.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tabs.Tests/SideScroll.Tabs.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Bookmark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Tests/SideScroll.Avalonia.Tests/SideScroll.Avalonia.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | // Includes Avalonia, Web Servers, and Core Libraries 7 | { 8 | "label": "build SideScroll", 9 | "command": "dotnet build SideScroll.sln", 10 | "type": "shell", 11 | "group": "build", 12 | "presentation": { 13 | "reveal": "silent" 14 | }, 15 | "problemMatcher": "$msCompile" 16 | }, 17 | ] 18 | } -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Samples/Controls/TabSampleTextArea.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Controls; 2 | using SideScroll.Resources; 3 | using SideScroll.Tabs; 4 | 5 | namespace SideScroll.Avalonia.Samples.Controls; 6 | 7 | public class TabSampleTextArea : ITab 8 | { 9 | public TabInstance Create() => new Instance(); 10 | 11 | private class Instance : TabInstance 12 | { 13 | public override void LoadUI(Call call, TabModel model) 14 | { 15 | model.MaxDesiredWidth = 800; 16 | 17 | var textArea = new TabTextArea(TextSamples.Plain); 18 | model.AddObject(textArea, true); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Bookmarks/TabLinks.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Bookmarks; 4 | 5 | public class TabLinks : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | public class Instance : TabInstance 10 | { 11 | public override void Load(Call call, TabModel model) 12 | { 13 | var linkManager = LinkManager.Instance!; 14 | 15 | model.Items = new List 16 | { 17 | new("Imported", new TabLinkCollection(linkManager.Imported)), 18 | new("Created", new TabLinkCollection(linkManager.Created)), 19 | }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Logo.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Collections; 2 | using System.Reflection; 3 | 4 | namespace SideScroll.Resources; 5 | 6 | public static class Logo 7 | { 8 | public const string LogoPath = "SideScroll.Resources.Logo"; 9 | 10 | public static Assembly Assembly => Assembly.GetExecutingAssembly(); 11 | 12 | public class Svg : NamedItemCollection 13 | { 14 | public static ResourceView SideScrollTranslucent => Get("SideScroll-Translucent"); 15 | 16 | public static ResourceView Get(string resourceName) => new(Assembly, LogoPath, "svg", resourceName, "svg"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleGridEditing.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Logs; 2 | 3 | namespace SideScroll.Tabs.Samples.DataGrid; 4 | 5 | public class TabSampleGridEditing : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | private readonly LogEntry _logEntry = new() 12 | { 13 | Text = "Test Entry", 14 | }; 15 | 16 | public override void Load(Call call, TabModel model) 17 | { 18 | // uses DataGrid internally, doesn't work well yet 19 | model.AddData(_logEntry); 20 | model.Editing = true; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts/SideScroll.Avalonia.Charts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | True 9 | \ 10 | 11 | 12 | True 13 | \ 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/TabIcons.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Resources; 2 | using SideScroll.Tabs.Toolbar; 3 | 4 | namespace SideScroll.Tabs.Samples; 5 | 6 | public class TabIcons : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | public override void Load(Call call, TabModel model) 13 | { 14 | TabToolbar toolbarSvg = new() 15 | { 16 | Buttons = Icons.Svg.Items 17 | .Select(memberInfo => new ToolButton(memberInfo.Key.Name, memberInfo.Value)) 18 | .ToList() 19 | }; 20 | model.AddObject(toolbarSvg); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Restore.svg: -------------------------------------------------------------------------------- 1 | 2 | window-restore-line 3 | 4 | 5 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Charts/TabSampleChartNoData.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Charts; 2 | using SideScroll.Time; 3 | 4 | namespace SideScroll.Tabs.Samples.Charts; 5 | 6 | public class TabSampleChartNoData : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | public override void Load(Call call, TabModel model) 13 | { 14 | var chartView = new ChartView("Animals"); 15 | chartView.AddSeries("Cats", new List()); 16 | chartView.AddSeries("Dogs", new List()); 17 | 18 | model.AddObject(chartView); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/MainWindow.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using SideScroll.Avalonia.Charts.LiveCharts; 3 | using SideScroll.Avalonia.Controls; 4 | using SideScroll.Avalonia.Samples.Tabs; 5 | using SideScroll.Tabs; 6 | 7 | namespace SideScroll.Avalonia.Samples; 8 | 9 | public class MainWindow : BaseWindow 10 | { 11 | public MainWindow() : base(Project.Load(SampleProjectSettings.Settings)) 12 | { 13 | LoadTab(new TabAvalonia()); 14 | 15 | LiveChartCreator.Register(); 16 | TabViewer.Toolbar?.AddRightControls(); 17 | 18 | Icon = new WindowIcon(Assets.Icons.SideScroll.Stream); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Search.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/Flyouts/MessageFlyout.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Media; 4 | 5 | namespace SideScroll.Avalonia.Controls.Flyouts; 6 | 7 | public class MessageFlyout : Flyout 8 | { 9 | public MessageFlyout(string text) 10 | { 11 | Placement = PlacementMode.Bottom; 12 | Content = new StackPanel 13 | { 14 | Margin = new Thickness(0), 15 | Spacing = 8, 16 | MaxWidth = 350, 17 | Children = 18 | { 19 | new TextBlock 20 | { 21 | Text = text, 22 | TextWrapping = TextWrapping.Wrap, 23 | Margin = new Thickness(0) 24 | }, 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/SideScroll.Serialize.Tests/DeepCloneTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace SideScroll.Serialize.Tests; 4 | 5 | [Category("DeepClone")] 6 | public class DeepCloneTests : SerializeBaseTest 7 | { 8 | [OneTimeSetUp] 9 | public void BaseSetup() 10 | { 11 | Initialize("DeepClone"); 12 | } 13 | 14 | [SetUp] 15 | public void Setup() 16 | { 17 | } 18 | 19 | class StringClass 20 | { 21 | public string Value = "value"; 22 | } 23 | 24 | [Test] 25 | public void DeepCloneStringField() 26 | { 27 | var input = new StringClass(); 28 | 29 | var output = input.DeepClone(); 30 | 31 | Assert.That(output.Value, Is.SameAs(input.Value)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Duplicate.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Tabs/TabTextFile.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Controls.TextEditor; 2 | using SideScroll.Tabs; 3 | using SideScroll.Utilities; 4 | 5 | namespace SideScroll.Avalonia.Tabs; 6 | 7 | public class TabTextFile(FilePath filePath) : ITab 8 | { 9 | public FilePath FilePath => filePath; 10 | 11 | public TabInstance Create() => new Instance(this); 12 | 13 | private class Instance(TabTextFile tab) : TabInstance 14 | { 15 | public override void LoadUI(Call call, TabModel model) 16 | { 17 | var tabAvaloniaEdit = new TabAvaloniaEdit(); 18 | tabAvaloniaEdit.Load(tab.FilePath.Path); 19 | model.AddObject(tabAvaloniaEdit, true); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabViewer.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleLoading.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Samples.Exceptions; 3 | 4 | namespace SideScroll.Tabs.Samples.Loading; 5 | 6 | [ListItem] 7 | public class TabSampleLoading 8 | { 9 | public static TabSampleLoadModel Load => new(); 10 | public static TabSampleLoadAsync LoadAsync => new(); 11 | public static TabSampleLoadItemProperties LoadItemProperties => new(); 12 | public static TabSampleLoadAsyncItemDelegate LoadAsyncDelegate => new(); 13 | public static TabSampleLoadAsyncItem LoadAsyncItem => new(); 14 | public static TabSampleSkip Skip => new(); 15 | public static TabSampleExceptions Exceptions => new(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Console/SideScroll.Demo.Console.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | false 6 | 7 | 8 | 9 | AnyCPU 10 | 11 | 12 | 13 | AnyCPU 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Samples/Controls/TabSampleControls.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Samples.Controls.CustomControl; 2 | using SideScroll.Tabs; 3 | using SideScroll.Tabs.Lists; 4 | 5 | namespace SideScroll.Avalonia.Samples.Controls; 6 | 7 | public class TabSampleControls : ITab 8 | { 9 | public TabInstance Create() => new Instance(); 10 | 11 | private class Instance : TabInstance 12 | { 13 | public override void Load(Call call, TabModel model) 14 | { 15 | model.Items = new List 16 | { 17 | new("Custom Control", new TabCustomControl()), 18 | new("Text Area", new TabSampleTextArea()), 19 | new("Flyouts", new TabSampleFlyout()), 20 | }; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabSeparator.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Charts/TabSampleCharts.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Samples.Charts; 4 | 5 | [ListItem] 6 | public class TabSampleCharts 7 | { 8 | public static TabSampleChartLists Lists => new(); 9 | public static TabSampleChartProperties Properties => new(); 10 | public static TabSampleChartDimensions Dimensions => new(); 11 | public static TabSampleChartTimeSeries TimeSeries => new(); 12 | public static TabSampleChartTimeSeriesFractional Fractional => new(); 13 | public static TabSampleChartSeriesCount SeriesCount => new(); 14 | public static TabSampleChartNoData NoData => new(); 15 | public static TabProcessMonitor ProcessMonitor => new(); 16 | } 17 | -------------------------------------------------------------------------------- /Tests/SideScroll.Network.Tests/SideScroll.Network.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/LeftArrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/LeftArrowCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Network/Http/HttpCachedCall.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace SideScroll.Network.Http; 4 | 5 | public class HttpCachedCall(Call call, HttpCache httpCache) : HttpCall(call) 6 | { 7 | public HttpCache HttpCache => httpCache; 8 | 9 | public override async Task GetBytesAsync(string uri) 10 | { 11 | byte[]? bytes = HttpCache.GetBytes(uri); 12 | if (bytes != null) 13 | return bytes; 14 | 15 | bytes = await base.GetBytesAsync(uri); 16 | HttpCache.AddEntry(uri, bytes); 17 | return bytes; 18 | } 19 | 20 | public override async Task GetStringAsync(string uri, string? accept = null) 21 | { 22 | byte[] bytes = await GetBytesAsync(uri); 23 | return Encoding.ASCII.GetString(bytes); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/PlayCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Utilities/ThreadUtils.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Utilities; 2 | 3 | /// 4 | /// Provides utilities for working with delayed task execution 5 | /// 6 | public static class ThreadUtils 7 | { 8 | /// 9 | /// Invokes an action after a specified delay in milliseconds 10 | /// 11 | public static void InvokeDelayed(int milliSecondsDelay, Action action) 12 | { 13 | Task.Delay(milliSecondsDelay).ContinueWith(_ => action()); 14 | } 15 | 16 | /// 17 | /// Invokes an action after a specified TimeSpan delay 18 | /// 19 | public static void InvokeDelayed(TimeSpan timeSpanDelay, Action action) 20 | { 21 | Task.Delay(timeSpanDelay).ContinueWith(_ => action()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Reset.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/RightArrowCircle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleArray.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Samples.DataGrid; 4 | 5 | [ListItem] 6 | public class TabSampleArray 7 | { 8 | public string[] StringArray => 9 | [ 10 | "Item 1", 11 | "Item 2", 12 | "Item 3", 13 | ]; 14 | 15 | public Pet[] Classes => 16 | [ 17 | new("Casper"), 18 | new("Panda Bear"), 19 | ]; 20 | 21 | // todo: this currently shows as a single column 22 | public int[,] MultiDimensional => new[,] 23 | { 24 | { 1, 2 }, 25 | { 3, 4 }, 26 | }; 27 | 28 | public bool[,] SingleItem => new bool[1, 1]; 29 | 30 | public class Pet(string name) 31 | { 32 | public string Name { get; set; } = name; 33 | 34 | public override string ToString() => Name; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleLoadAsyncItem.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Samples.Loading; 5 | 6 | public class TabSampleLoadAsyncItem : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | public override void Load(Call call, TabModel model) 13 | { 14 | model.Items = new List 15 | { 16 | new("Test Item", new TestItem()), 17 | }; 18 | } 19 | 20 | private class TestItem 21 | { 22 | public int Integer { get; set; } 23 | 24 | [Item] 25 | public async Task Text(Call call) 26 | { 27 | await Task.Delay(2000); 28 | return "Text"; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Objects/TabSampleSubClassProperty.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.Objects; 2 | 3 | public class TabSampleSubClassProperty : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | public override void Load(Call call, TabModel model) 10 | { 11 | model.Items = new List 12 | { 13 | new() 14 | }; 15 | } 16 | } 17 | 18 | private class ParentClass 19 | { 20 | public ChildClass Child { get; set; } = new(); 21 | 22 | public override string ToString() => Child.ToString(); 23 | } 24 | 25 | 26 | private class ChildClass 27 | { 28 | public string Text { get; set; } = "test"; 29 | 30 | public override string ToString() => Text; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Extensions/AvaloniaExtensions.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | 3 | namespace SideScroll.Avalonia.Extensions; 4 | 5 | public static class AvaloniaExtensions 6 | { 7 | public static Color AsAvaloniaColor(this System.Drawing.Color color) 8 | { 9 | return new Color(color.A, color.R, color.G, color.B); 10 | } 11 | 12 | public static System.Drawing.Color AsSystemColor(this Color color) 13 | { 14 | return System.Drawing.Color.FromArgb(color.A, color.R, color.G, color.B); 15 | } 16 | 17 | public static Color WithAlpha(this Color color, byte alpha) 18 | { 19 | return new Color(alpha, color.R, color.G, color.B); 20 | } 21 | 22 | public static T Also(this T obj, Action configure) 23 | { 24 | configure(obj); 25 | return obj; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SideScroll 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |

15 | 16 | SideScroll Logo 17 | 18 |
Loading 19 |

20 |
21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace SideScroll.Avalonia.Samples; 6 | 7 | public class App : Application 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | public override void OnFrameworkInitializationCompleted() 15 | { 16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) 17 | { 18 | desktopLifetime.MainWindow = new MainWindow(); 19 | } 20 | else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) 21 | { 22 | singleViewLifetime.MainView = new MainView(); 23 | } 24 | 25 | base.OnFrameworkInitializationCompleted(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleSkip.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Samples.Loading; 4 | 5 | public class TabSampleSkip : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | public override void Load(Call call, TabModel model) 12 | { 13 | var sampleItems = new List 14 | { 15 | new(1, "Item 1"), 16 | }; 17 | 18 | model.Items = new List 19 | { 20 | new("Sample Items", sampleItems), 21 | }; 22 | } 23 | } 24 | } 25 | 26 | public class SampleItem(int id, string name) 27 | { 28 | public int Id { get; set; } = id; 29 | public string Name { get; set; } = name; 30 | 31 | public override string ToString() => Name; 32 | } 33 | -------------------------------------------------------------------------------- /Tests/SideScroll.Serialize.Tests/SideScroll.Serialize.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Cancel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Star.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/FileViewer/TabDrives.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Tools.FileViewer; 5 | 6 | [PrivateData] 7 | public class TabDrives(FileSelectorOptions? fileSelectorOptions = null) : ITab 8 | { 9 | public FileSelectorOptions? FileSelectorOptions { get; set; } = fileSelectorOptions; 10 | 11 | public TabInstance Create() => new Instance(this); 12 | 13 | public class Instance(TabDrives tab) : TabInstance 14 | { 15 | public override void Load(Call call, TabModel model) 16 | { 17 | DriveInfo[] drives = DriveInfo.GetDrives(); 18 | 19 | model.Items = drives 20 | .Select(drive => new ListPair(drive.Name, drive.VolumeLabel, new TabDirectory(drive.Name, tab.FileSelectorOptions))) 21 | .ToList(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Logs/LogWriterConsole.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Logs; 2 | 3 | public class LogWriterConsole 4 | { 5 | public Log Log { get; } 6 | 7 | public SynchronizationContext Context { get; set; } 8 | 9 | public override string ToString() => "Console"; 10 | 11 | public LogWriterConsole(Log log) 12 | { 13 | Log = log; 14 | 15 | Context = SynchronizationContext.Current ?? new(); 16 | 17 | log.OnMessage += LogEntry_OnMessage; 18 | } 19 | 20 | private static void LogEntry_OnMessage(object? sender, LogMessageEventArgs e) 21 | { 22 | string indentation = new('\t', e.Entries.Count); 23 | 24 | LogEntry newLog = e.Entries[0]; 25 | //string line = log.Created.ToString("yyyy-MM-dd HH:mm:ss") + indentation + log.ToString(); 26 | 27 | Console.WriteLine(indentation + newLog.Message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/ImageColorView.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | using SideScroll.Resources; 3 | 4 | namespace SideScroll.Avalonia.Themes; 5 | 6 | public class ImageColorView(ResourceView resourceView, string colorName, string highlightColorName) : IResourceView 7 | { 8 | public string ResourceType => resourceView.ResourceType; 9 | public string Path => resourceView.Path; 10 | 11 | public Stream Stream => resourceView.Stream; 12 | 13 | public Color? Color => SideScrollTheme.GetBrush(colorName).Color; 14 | public Color? HighlightColor => SideScrollTheme.GetBrush(highlightColorName).Color; 15 | 16 | public static ImageColorView CreateAlternate(ResourceView resourceView) 17 | { 18 | return new ImageColorView(resourceView, "IconAltForegroundBrush", "IconAltForegroundHighlightBrush"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Close.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabAvaloniaEdit.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabSplitGrid.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleLoadItemProperties.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Samples.Loading; 4 | 5 | public class TabSampleLoadItemProperties : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | public override void Load(Call call, TabModel model) 12 | { 13 | model.Items = new List 14 | { 15 | new("Test Item", new TestItem()), 16 | }; 17 | } 18 | } 19 | 20 | private class TestItem 21 | { 22 | public int Integer { get; set; } 23 | 24 | private string? _text; 25 | public string Text 26 | { 27 | get 28 | { 29 | if (_text == null) 30 | { 31 | Thread.Sleep(5000); 32 | _text = "Text"; 33 | } 34 | return _text; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/TabSampleProcess.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tasks; 2 | using SideScroll.Utilities; 3 | 4 | namespace SideScroll.Tabs.Samples; 5 | 6 | public class TabSampleProcess : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | public override void Load(Call call, TabModel model) 13 | { 14 | model.Actions = new List 15 | { 16 | new TaskDelegate("Open Folder", OpenFolder, true), 17 | new TaskDelegate("Open Browser", OpenBrowser, true), 18 | }; 19 | } 20 | 21 | private static void OpenFolder(Call call) 22 | { 23 | ProcessUtils.OpenFolder(Directory.GetCurrentDirectory()); 24 | } 25 | 26 | private static void OpenBrowser(Call call) 27 | { 28 | ProcessUtils.OpenBrowser("https://wikipedia.org"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/TextSamples.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Collections; 2 | using System.Reflection; 3 | 4 | namespace SideScroll.Resources; 5 | 6 | public class TextSamples : NamedItemCollection 7 | { 8 | public const string SamplePath = "SideScroll.Resources.Samples"; 9 | 10 | public static Assembly Assembly => Assembly.GetExecutingAssembly(); 11 | 12 | public static string Plain => GetText("SolarSystem", "txt"); 13 | public static string Json => GetText("SolarSystem", "json"); 14 | public static string Xml => GetText("SolarSystem", "xml"); 15 | 16 | public static ResourceView Get(string resourceName, string resourceType) => new(Assembly, SamplePath, "Text", resourceName, resourceType); 17 | public static string GetText(string resourceName, string resourceType) => Get(resourceName, resourceType).ReadText(); 18 | } 19 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/TabTools.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Collections; 2 | using SideScroll.Tabs.Lists; 3 | using SideScroll.Tabs.Tools.FileViewer; 4 | 5 | namespace SideScroll.Tabs.Tools; 6 | 7 | public class TabTools : ITab 8 | { 9 | public TabInstance Create() => new Instance(); 10 | 11 | private class Instance : TabInstance 12 | { 13 | private ItemCollectionUI _items = []; 14 | 15 | public override void Load(Call call, TabModel model) 16 | { 17 | model.Items = _items = 18 | [ 19 | new ListItem("File Viewer", new TabFileViewer()), 20 | new ListItem("File Selector", new TabFileViewer(SelectFile)) 21 | ]; 22 | } 23 | 24 | private void SelectFile(Call call, string path) 25 | { 26 | string filename = Path.GetFileName(path); 27 | _items.Add(new ListItem(filename, new FileView(path))); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v4 21 | with: 22 | dotnet-version: 8.0.x 23 | - name: Install .NET WASM workload 24 | run: dotnet workload install wasm-tools-net8 25 | - name: Restore dependencies 26 | run: dotnet restore 27 | - name: Build 28 | run: dotnet build --no-restore 29 | - name: Test 30 | run: dotnet test --no-build --verbosity normal 31 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleBytes.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Samples.DataGrid; 4 | 5 | public class TabSampleBytes : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | public override void Load(Call call, TabModel model) 12 | { 13 | model.Items = new List 14 | { 15 | new("100 Bytes", Create(100)), 16 | new("1,000 Bytes", Create(1_000)), 17 | new("10,000 Bytes", Create(10_000)), 18 | new("100,000 Bytes", Create(100_000)), 19 | new("1,000,000 Bytes", Create(1_000_000)), 20 | }; 21 | } 22 | 23 | private byte[] Create(int size) 24 | { 25 | var bytes = new byte[size]; 26 | for (int i = 0; i < size; i++) 27 | { 28 | bytes[i] = (byte)i; 29 | } 30 | return bytes; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts.LiveCharts/Themes/TabLiveChart.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Lists/ListItemTemplate.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using System.ComponentModel; 3 | 4 | namespace SideScroll.Tabs.Lists; 5 | 6 | // implement INotifyPropertyChanged to prevent memory leaks 7 | public class ListItem(TKey key, TValue value, object obj) : INotifyPropertyChanged 8 | { 9 | public TKey Key { get; set; } = key; 10 | public TValue Value { get; set; } = value; 11 | 12 | [HiddenColumn, InnerValue] 13 | public object Object { get; set; } = obj; 14 | 15 | [HiddenColumn] 16 | public bool IsAutoSelectable { get; set; } = true; 17 | 18 | #pragma warning disable 414 19 | public event PropertyChangedEventHandler? PropertyChanged; 20 | 21 | public override string ToString() => Key?.ToString() ?? ""; 22 | } 23 | 24 | /* 25 | Not used anywhere. Remove? 26 | Uses: 27 | Passing dynamic types? 28 | Setting return type? 29 | */ 30 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleLoadAsyncItemDelegate.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Samples.Loading; 4 | 5 | public class TabSampleLoadAsyncItemDelegate : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | public override void Load(Call call, TabModel model) 12 | { 13 | model.Items = new List 14 | { 15 | new ListDelegate(SlowItemAsync), 16 | }; 17 | } 18 | 19 | // todo: fix, this is being called twice and blocking the UI the 1st time 20 | // Preloading doesn't trigger for methods, and most results won't be cached 21 | // Need a new CollectionView that can preload and cache? 22 | private static async Task SlowItemAsync(Call call) 23 | { 24 | await Task.Delay(2000); 25 | return "finished"; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Docs/Dev/ProjectSetup.md: -------------------------------------------------------------------------------- 1 | # Project Setup 2 | 3 | ## Install SideScroll dotnet Template 4 | 5 | ```bash 6 | dotnet new install SideScroll.Templates 7 | ``` 8 | - See [SideScroll.Templates](https://github.com/SideScrollUI/SideScroll.Templates) for more details 9 | 10 | ## Create Project using Template 11 | 12 | To create a new SideScroll app from the template: 13 | 14 | ```bash 15 | dotnet new sidescroll.app -o MyApp 16 | ``` 17 | 18 | If you want to create a new Solution with the app split into a Library and Program app: 19 | 20 | ```bash 21 | dotnet new sidescroll.sln -o MyApp 22 | ``` 23 | 24 | ## Adding Tabs 25 | 26 | See [Adding Tabs](AddingTabs.md) for more 27 | 28 | ## Sample Projects 29 | 30 | - [Tab Samples](../../Libraries/SideScroll.Avalonia.Samples/MainWindow.cs) - Inside this repo 31 | - [Video Game Database](https://github.com/SideScrollUI/VideoGamesDB) - Sample external project 32 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/Undo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 5.3.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/SideScroll.Avalonia.Tests/AvaloniaFieldValueConverterTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Avalonia.Controls.Converters; 3 | using SideScroll.Time; 4 | using System.Globalization; 5 | 6 | namespace SideScroll.Avalonia.Tests; 7 | 8 | public class AvaloniaFieldValueConverterTests 9 | { 10 | [SetUp] 11 | public void Setup() 12 | { 13 | } 14 | 15 | [Test] 16 | public void ConvertDateTime() 17 | { 18 | TimeZoneView.Current = TimeZoneView.Utc; 19 | DateTime dateTime = new(2000, 1, 2, 0, 0, 0, DateTimeKind.Utc); 20 | FormatValueConverter converter = new(); 21 | string converted = (string)converter.Convert(dateTime, typeof(string), null, CultureInfo.CurrentCulture)!; 22 | DateTime processedDateTime = (DateTime)converter.Convert(converted, typeof(DateTime), null, CultureInfo.CurrentCulture)!; 23 | Assert.That(processedDateTime, Is.EqualTo(dateTime)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/Redo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 5.3.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/SideScroll.Demo.Avalonia.Browser.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0-browser 5 | Exe 6 | true 7 | false 8 | false 9 | true 10 | true 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "SideScroll.Demo.Avalonia.Desktop", 6 | "console": "integratedTerminal", 7 | "type": "coreclr", 8 | "request": "launch", 9 | "preLaunchTask": "build SideScroll", 10 | "program": "${workspaceFolder}/Programs/SideScroll.Demo.Avalonia.Desktop/bin/Debug/net8.0/SideScroll.Demo.Avalonia.Desktop.dll", 11 | "args": [], 12 | "cwd": "${workspaceFolder}/Programs/SideScroll.Demo.Avalonia.Desktop", 13 | "stopAtEntry": false, 14 | "internalConsoleOptions": "openOnSessionStart" 15 | }, 16 | { 17 | "name": ".NET Core Attach", 18 | "type": "coreclr", 19 | "request": "attach", 20 | "processId": "${command:pickProcess}" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/PanelLeftContract.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Desktop/SideScroll.Demo.Avalonia.Desktop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WinExe 5 | 6 | false 7 | 8 | 9 | 10 | x64 11 | 12 | 13 | x64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/Tabs/TabAvalonia.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Samples.Charts; 2 | using SideScroll.Avalonia.Samples.Controls; 3 | using SideScroll.Avalonia.Tabs; 4 | using SideScroll.Tabs; 5 | using SideScroll.Tabs.Bookmarks; 6 | using SideScroll.Tabs.Lists; 7 | using SideScroll.Tabs.Samples; 8 | 9 | namespace SideScroll.Avalonia.Samples.Tabs; 10 | 11 | public class TabAvalonia : ITab 12 | { 13 | public TabInstance Create() => new Instance(); 14 | 15 | private class Instance : TabInstance 16 | { 17 | public override void Load(Call call, TabModel model) 18 | { 19 | model.Items = new List 20 | { 21 | new("Samples", new TabSamples()), 22 | new("Controls", new TabSampleControls()), 23 | new("Charts", new TabCustomCharts()), 24 | new("Links", new TabLinks()), 25 | new("Settings", new TabAvaloniaSettings()), 26 | }; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Refresh.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleFilter.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.DataGrid; 2 | 3 | public class TabSampleFilter : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | public override void Load(Call call, TabModel model) 10 | { 11 | string characters = "abcdefghijklmn"; 12 | List items = []; 13 | for (int i = 0; i < 10; i++) 14 | { 15 | var item = new TestFilterItem("Item " + i, i) 16 | { 17 | Child = new TestFilterItem(characters[i].ToString(), i) 18 | }; 19 | 20 | items.Add(item); 21 | } 22 | 23 | model.Items = items; 24 | model.ShowSearch = true; 25 | } 26 | } 27 | 28 | public record TestFilterItem(string Text, int Number) 29 | { 30 | //[InnerValue] 31 | public TestFilterItem? Child; 32 | 33 | public override string ToString() => Text; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Docs/Dev/Logs.md: -------------------------------------------------------------------------------- 1 | # Logs 2 | 3 | ```csharp 4 | Log log = new(); 5 | log.Add("New Log entry", 6 | new Tag("Name", "value"), 7 | new Tag("Count", 123)); 8 | ``` 9 | * Logs have a tree structure and can be nested as deep as necessary. 10 | 11 | ## Tags 12 | 13 | * All the tags get added to the log line. 14 | * You can embed any object in the Tag `Value`, but these can consume lots of memory if you're not careful. These values can later be viewed in the log entry's `Tags` properties. 15 | 16 | ## Timers 17 | 18 | * You can time any operation by using a Log or Call Timer with the `using` operator 19 | 20 | ### Log Timers 21 | 22 | ```csharp 23 | using (LogTimer logTimer = call.Log.Timer("Doing work")) 24 | { 25 | logTimer.Add("Child log entry"); 26 | } 27 | ``` 28 | 29 | ### Call Timers 30 | 31 | ```csharp 32 | using CallTimer callTimer = call.Timer("Doing work"); 33 | 34 | callTimer.Log.Add("Child log entry"); 35 | ``` 36 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Clip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleNullableArray.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.DataGrid; 2 | 3 | public class TabSampleNullableArray : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | public override void Load(Call call, TabModel model) 10 | { 11 | var testItems = new TestItem?[10]; 12 | 13 | for (int i = 0; i < testItems.Length; i++) // Should we allow null values too? i += 2 14 | { 15 | testItems[i] = new TestItem 16 | { 17 | SmallNumber = i, 18 | BigNumber = i * 1000, 19 | }; 20 | } 21 | //model.Items = testItems; 22 | model.AddData(testItems); 23 | } 24 | } 25 | 26 | private struct TestItem 27 | { 28 | public int SmallNumber { get; set; } 29 | public long BigNumber { get; set; } 30 | 31 | public readonly override string ToString() => SmallNumber.ToString(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Tasks/TaskAction.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tasks; 2 | 3 | /// 4 | /// A task creator that wraps a simple Action delegate for execution 5 | /// 6 | public class TaskAction : TaskCreator 7 | { 8 | public Action Action { get; } 9 | 10 | public override string? ToString() => Label; 11 | 12 | /// 13 | /// Initializes a new task action with the specified label and action 14 | /// 15 | public TaskAction(string label, Action action, bool useTask = false) 16 | { 17 | Label = label; 18 | Action = action; 19 | UseTask = useTask; 20 | UseBackgroundThread = useTask; 21 | } 22 | 23 | /// 24 | /// Creates an action that will invoke the wrapped action 25 | /// 26 | public override Action CreateAction(Call call) 27 | { 28 | return InvokeAction; 29 | } 30 | 31 | private void InvokeAction() 32 | { 33 | Action.Invoke(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Utilities/LogUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace SideScroll.Utilities; 4 | 5 | /// 6 | /// Provides utilities for logging exceptions to files 7 | /// 8 | public static class LogUtils 9 | { 10 | /// 11 | /// Saves an exception to a log file and writes it to the console 12 | /// 13 | public static void Save(string directory, string filePrefix, Exception e) 14 | { 15 | string filename = filePrefix + ".Exception." + FileUtils.TimestampString + ".log"; 16 | string filePath = Paths.Combine(directory, filename); 17 | string message = e.ToString(); 18 | 19 | Directory.CreateDirectory(directory); 20 | File.WriteAllText(filePath, message); 21 | 22 | Console.WriteLine("Exception stacktrace written to:"); 23 | Console.WriteLine(filePath); 24 | Console.WriteLine(); 25 | Console.WriteLine(message); 26 | 27 | Debug.Fail(message); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/TabSampleBookmarks.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Serialize; 3 | using SideScroll.Tabs.Bookmarks; 4 | using SideScroll.Tabs.Lists; 5 | 6 | namespace SideScroll.Tabs.Samples; 7 | 8 | [TabRoot, PublicData] 9 | public class TabSampleBookmarks : ITab 10 | { 11 | public override string ToString() => "Bookmarks"; 12 | 13 | public TabInstance Create() => new Instance(); 14 | 15 | private class Instance : TabInstance 16 | { 17 | public override void LoadUI(Call call, TabModel model) 18 | { 19 | BookmarkNavigator navigator = Project.Navigator.DeepClone(call); // Clone from UI thread only 20 | navigator.History.RemoveAt(navigator.History.Count - 1); // Remove the current in progress bookmark 21 | navigator.CurrentIndex = navigator.History.Count; 22 | 23 | model.Items = new List 24 | { 25 | new("Navigator (snapshot)", navigator), 26 | }; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Network/Http/HttpCacheManager.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Network.Http; 2 | 3 | public class HttpCacheManager 4 | { 5 | public HttpMemoryCache MemoryCache { get; set; } = new(); 6 | 7 | private readonly Dictionary _httpCaches = []; 8 | 9 | // should we keep the imports open all the time? 10 | // should we be returning disposable references? 11 | public HttpCache OpenCache(string path) 12 | { 13 | if (_httpCaches.TryGetValue(path, out HttpCache? httpCache)) 14 | return httpCache; 15 | 16 | httpCache = new HttpCache(path, true); 17 | _httpCaches[path] = httpCache; 18 | return httpCache; 19 | } 20 | 21 | public void DeleteHttpCache(string path) 22 | { 23 | if (_httpCaches.TryGetValue(path, out HttpCache? httpCache)) 24 | { 25 | httpCache.Dispose(); 26 | _httpCaches.Remove(path); 27 | } 28 | 29 | if (Directory.Exists(path)) 30 | { 31 | Directory.Delete(path, true); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Shutter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Desktop/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using SideScroll.Avalonia.Controls.ScreenCapture; 3 | using SideScroll.Avalonia.Controls.Viewer; 4 | using SideScroll.Avalonia.Samples; 5 | using SideScroll.Utilities; 6 | 7 | namespace SideScroll.Demo.Avalonia.Desktop; 8 | 9 | internal static class Program 10 | { 11 | [STAThread] 12 | private static int Main(string[] args) 13 | { 14 | AppBuilder builder = BuildAvaloniaApp(); 15 | 16 | TabViewer.Plugins.Add(new ScreenCapture.TabViewerPlugin()); 17 | 18 | try 19 | { 20 | return builder.StartWithClassicDesktopLifetime(args); 21 | } 22 | catch (Exception e) 23 | { 24 | var settings = SampleProjectSettings.Settings; 25 | LogUtils.Save(settings.ExceptionsPath, settings.Name!, e); 26 | return 1; 27 | } 28 | } 29 | 30 | private static AppBuilder BuildAvaloniaApp() 31 | => AppBuilder.Configure() 32 | .UsePlatformDetect() 33 | .WithInterFont() 34 | .LogToTrace(); 35 | } 36 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabHeader.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/TabCheckBox.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Data; 3 | using Avalonia.Layout; 4 | using SideScroll.Tabs.Lists; 5 | 6 | namespace SideScroll.Avalonia.Controls; 7 | 8 | public class TabCheckBox : CheckBox 9 | { 10 | protected override Type StyleKeyOverride => typeof(CheckBox); 11 | 12 | public TabCheckBox() 13 | { 14 | HorizontalAlignment = HorizontalAlignment.Stretch; 15 | MaxWidth = TabForm.ControlMaxWidth; 16 | //Margin = new Thickness(2, 2); 17 | //Padding = new Thickness(6, 3); 18 | } 19 | 20 | public TabCheckBox(ListProperty property) : this() 21 | { 22 | IsEnabled = property.IsEditable; 23 | Bind(property); 24 | } 25 | 26 | public void Bind(ListProperty property) 27 | { 28 | var binding = new Binding(property.PropertyInfo.Name) 29 | { 30 | Mode = property.IsEditable ? BindingMode.TwoWay : BindingMode.OneWay, 31 | Source = property.Object, 32 | }; 33 | Bind(IsCheckedProperty, binding); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/AvaloniaAssets.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media.Imaging; 2 | using SideScroll.Resources; 3 | using System.Reflection; 4 | 5 | namespace SideScroll.Avalonia; 6 | 7 | public static class AvaloniaAssets 8 | { 9 | public const string AssetPath = "SideScroll.Avalonia.Assets"; 10 | 11 | public static Assembly Assembly => Assembly.GetExecutingAssembly(); 12 | 13 | public static Bitmap GetBitmap(string name) 14 | { 15 | Assembly assembly = Assembly.GetExecutingAssembly(); 16 | 17 | using Stream stream = assembly.GetManifestResourceStream(name)!; 18 | return new Bitmap(stream); 19 | } 20 | 21 | public static ResourceView Get(string resourceName, string resourceType) => new(Assembly, AssetPath, "Themes", resourceName, resourceType); 22 | public static string GetText(string resourceName, string resourceType) => Get(resourceName, resourceType).ReadText(); 23 | 24 | public static class Themes 25 | { 26 | public static string Hybrid => GetText("Hybrid", "json"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleGridHashSet.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.DataGrid; 2 | 3 | public class TabSampleGridHashSet : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | private HashSet _items = []; 10 | 11 | public override void Load(Call call, TabModel model) 12 | { 13 | _items = []; 14 | AddEntries(); 15 | model.AddData(_items); 16 | 17 | // HashSet not observable 18 | /*model.Actions = new List 19 | { 20 | new TaskDelegate("Add Entries", AddEntries), 21 | };*/ 22 | } 23 | 24 | private void AddEntries() 25 | { 26 | for (int i = 0; i < 20; i++) 27 | { 28 | int index = _items.Count; 29 | var testItem = new TabSampleGridCollectionSize.TestItem 30 | { 31 | SmallNumber = index, 32 | }; 33 | testItem.BigNumber += index; 34 | _items.Add(testItem); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/PadNote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleInstanceListItems.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Samples.DataGrid; 4 | 5 | public class TabSampleInstanceListItems : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | private class Instance : TabInstance 10 | { 11 | public bool BooleanField = true; // Ignored for now, GetListItems() doesn't support fields 12 | public bool BooleanProperty { get; set; } = true; 13 | 14 | public string StringProperty => "StringProperty"; 15 | 16 | [Item] 17 | public string Method() => "Result"; 18 | 19 | [Item] 20 | public async Task AsyncMethodAsync() => await MethodAsync(); 21 | 22 | [Item] 23 | public Task TaskMethodAsync() => MethodAsync(); 24 | 25 | public override void Load(Call call, TabModel model) 26 | { 27 | model.Items = GetListItems(); 28 | } 29 | 30 | private static async Task MethodAsync() 31 | { 32 | await Task.Delay(10); 33 | return "Result"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabTextArea.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/SideScroll.Tabs.Tools.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SideScroll.Tabs.Tools 5 | SideScroll.Tabs.Tools 6 | 7 | 8 | 9 | AnyCPU 10 | 11 | 12 | 13 | AnyCPU 14 | 15 | 16 | 17 | 18 | True 19 | \ 20 | 21 | 22 | True 23 | \ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Docs/Dev/Development.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | ## Install .NET 8 SDK 4 | 5 | - [Download .NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) 6 | 7 | ## Git Clone 8 | 9 | ``` 10 | git clone https://github.com/SideScrollUI/SideScroll.git 11 | cd SideScroll 12 | ``` 13 | 14 | ## Build and Run 15 | 16 | - Start Sample Application 17 | ``` 18 | dotnet build 19 | dotnet run --project Programs/SideScroll.Demo.Avalonia.Desktop/SideScroll.Demo.Avalonia.Desktop.csproj 20 | ``` 21 | 22 | ## Avalonia UI 23 | 24 | * SideScroll is built on top of the [Avalonia UI](https://github.com/AvaloniaUI/Avalonia) framework, so be sure to also check out those libraries for more custom controls and advanced behaviors. 25 | 26 | ## Links 27 | 28 | * [Project Setup](ProjectSetup.md) 29 | * [Adding Tabs](AddingTabs.md) 30 | * [Tab Forms](TabForms.md) 31 | * [DataRepos](DataRepos.md) 32 | * [Serializer](Serializer.md) 33 | * [Logs](Logs.md) 34 | * [Bookmarks](Bookmarks.md) 35 | * [Charts](Charts.md) 36 | * [Projects](Projects.md) 37 | * [Building](Building.md) 38 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Network/SideScroll.Network.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | SideScroll.Network 6 | SideScroll.Network 7 | 8 | 9 | 10 | AnyCPU 11 | 12 | 13 | 14 | AnyCPU 15 | 16 | 17 | 18 | 19 | True 20 | \ 21 | 22 | 23 | True 24 | \ 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/SideScroll.Tabs.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SideScroll.Tabs.Samples 5 | SideScroll.Tabs.Samples 6 | 7 | 8 | 9 | AnyCPU 10 | 11 | 12 | 13 | AnyCPU 14 | 15 | 16 | 17 | 18 | True 19 | \ 20 | 21 | 22 | True 23 | \ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/PanelLeftExpand.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/ResourceView.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Reflection; 3 | 4 | namespace SideScroll.Resources; 5 | 6 | public interface IResourceView 7 | { 8 | string ResourceType { get; } 9 | string Path { get; } 10 | 11 | Stream Stream { get; } 12 | } 13 | 14 | public record ResourceView(Assembly Assembly, string BasePath, string GroupPath, string ResourceName, string ResourceType) : IResourceView 15 | { 16 | public string Path => $"{BasePath}.{GroupPath}.{ResourceName}.{ResourceType}"; 17 | 18 | public Stream Stream => Assembly.GetManifestResourceStream(Path)!; 19 | 20 | public string ReadText() => new StreamReader(Stream).ReadToEnd(); 21 | } 22 | 23 | public class ImageResourceView(ResourceView resourceView, Color? color = null, Color? highlightColor = null) : IResourceView 24 | { 25 | public string ResourceType => resourceView.ResourceType; 26 | public string Path => resourceView.Path; 27 | 28 | public Stream Stream => resourceView.Stream; 29 | 30 | public Color? Color => color; 31 | public Color? HighlightColor => highlightColor; 32 | } 33 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Tasks/TaskMethod.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace SideScroll.Tasks; 4 | 5 | /// 6 | /// A task creator that wraps a method from an object using reflection for dynamic task execution 7 | /// 8 | public class TaskMethod : TaskCreator 9 | { 10 | public MethodInfo MethodInfo { get; } 11 | public object Object { get; } // Object to invoke method for 12 | 13 | public override string ToString() => MethodInfo.Name; 14 | 15 | /// 16 | /// Initializes a new task method with the specified method info and target object 17 | /// 18 | public TaskMethod(MethodInfo methodInfo, object obj) 19 | { 20 | MethodInfo = methodInfo; 21 | Object = obj; 22 | 23 | Label = methodInfo.Name; 24 | } 25 | 26 | /// 27 | /// Creates an action that will invoke the method on the target object 28 | /// 29 | public override Action CreateAction(Call call) 30 | { 31 | return () => RunMethod(call); 32 | } 33 | 34 | private void RunMethod(Call call) 35 | { 36 | MethodInfo.Invoke(Object, [call]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Lists/ListPairObject.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using System.ComponentModel; 3 | 4 | namespace SideScroll.Tabs.Lists; 5 | 6 | // implement INotifyPropertyChanged to prevent memory leaks 7 | public class ListPair(object key, object? value, object? obj = null, int? maxDesiredWidth = null, int? maxDesiredHeight = null) 8 | : IListPair, IListItem, INotifyPropertyChanged, IMaxDesiredWidth, IMaxDesiredHeight 9 | { 10 | public object Key { get; set; } = key; 11 | 12 | [StyleValue] 13 | public object? Value { get; set; } = value; 14 | 15 | [HiddenColumn, InnerValue] 16 | public object? Object { get; set; } = obj ?? value; 17 | 18 | [HiddenColumn] 19 | public bool IsAutoSelectable { get; set; } = true; 20 | 21 | [HiddenColumn] 22 | public int? MaxDesiredWidth { get; set; } = maxDesiredWidth; 23 | 24 | [HiddenColumn] 25 | public int? MaxDesiredHeight { get; set; } = maxDesiredHeight; 26 | 27 | #pragma warning disable 414 28 | public event PropertyChangedEventHandler? PropertyChanged; 29 | 30 | public override string ToString() => Key?.ToString() ?? ""; 31 | } 32 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Demo/TabSampleDemo.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Resources; 3 | using SideScroll.Tabs.Samples.Charts; 4 | using SideScroll.Tabs.Samples.DataGrid; 5 | using SideScroll.Tabs.Samples.Forms; 6 | using SideScroll.Tabs.Samples.Forms.Todo; 7 | using SideScroll.Tabs.Samples.Objects; 8 | using SideScroll.Tabs.Tools.FileViewer; 9 | 10 | namespace SideScroll.Tabs.Samples.Demo; 11 | 12 | [ListItem, PublicData] 13 | public class TabSampleDemo 14 | { 15 | public override string ToString() => "Demo"; 16 | 17 | public static TabDemoPlanets Planets => new(); 18 | public static TabPlanetCharts Charts => new(); 19 | public static TabSampleTodos Todo => new(); 20 | public static TabSampleGridCollectionSize Collections => new(); 21 | public static TabSampleObjectMembers Objects => new(); 22 | public static TabSampleFormDataTabs Forms => new(); 23 | public static string Json => TextSamples.Json; 24 | public static TabFileViewer FileViewer => new(); 25 | public static TabProcessMonitor ProcessMonitor => new(); 26 | public static TabSampleDemo Copy => new(); 27 | } 28 | -------------------------------------------------------------------------------- /Libraries/SideScroll/SideScroll.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SideScroll 5 | 6 | 7 | 8 | AnyCPU 9 | 10 | 11 | 12 | AnyCPU 13 | 14 | 15 | 16 | 17 | True 18 | \ 19 | 20 | 21 | True 22 | \ 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Tests/SideScroll.Avalonia.Tests/AvaloniaDataBindingTests.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Data; 3 | using NUnit.Framework; 4 | 5 | namespace SideScroll.Avalonia.Tests; 6 | 7 | public class AvaloniaDataBindingTests 8 | { 9 | [SetUp] 10 | public void Setup() 11 | { 12 | } 13 | 14 | private class TestItem 15 | { 16 | public string? Name { get; set; } 17 | } 18 | 19 | [Test] 20 | public void Test1() 21 | { 22 | TestBind(1); 23 | } 24 | 25 | [Test] 26 | public void Test10() 27 | { 28 | TestBind(10); 29 | } 30 | 31 | /* 32 | [Test] 33 | public void Test10000() 34 | { 35 | TestBind(10_000); 36 | }*/ 37 | 38 | private static void TestBind(int count) 39 | { 40 | Binding binding = new() 41 | { 42 | Path = nameof(TestItem.Name), 43 | Mode = BindingMode.OneWay, // copying a value to the clipboard triggers an infinite loop without this? 44 | }; 45 | 46 | for (int i = 0; i < count; i++) 47 | { 48 | TextBlock textBlock = new(); 49 | //var testItem = new TestItem(); 50 | 51 | textBlock.Bind(TextBlock.TextProperty, binding); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/SideScroll.Avalonia.Samples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Stats.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Serialize/Atlas/TypeRepos/TypeRepoDecimal.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Serialize.Atlas.Schema; 2 | 3 | namespace SideScroll.Serialize.Atlas.TypeRepos; 4 | 5 | public class TypeRepoDecimal(Serializer serializer, TypeSchema typeSchema) : TypeRepo(serializer, typeSchema) 6 | { 7 | public class Creator : IRepoCreator 8 | { 9 | public TypeRepo? TryCreateRepo(Serializer serializer, TypeSchema typeSchema) 10 | { 11 | if (CanAssign(typeSchema.Type)) 12 | { 13 | return new TypeRepoDecimal(serializer, typeSchema); 14 | } 15 | return null; 16 | } 17 | } 18 | 19 | public static bool CanAssign(Type? type) 20 | { 21 | return type == typeof(decimal); 22 | } 23 | 24 | public override void SaveObject(BinaryWriter writer, object obj) 25 | { 26 | writer.Write((decimal)obj); 27 | } 28 | 29 | protected override object? CreateObject(int objectIndex) 30 | { 31 | return Reader!.ReadDecimal(); 32 | } 33 | 34 | public override void Clone(object source, object dest) 35 | { 36 | // assigning won't do anything since it's not a ref 37 | throw new SerializerException("Not cloneable"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleEnums.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | 3 | namespace SideScroll.Tabs.Samples.DataGrid; 4 | 5 | public class TabSampleEnums : ITab 6 | { 7 | public TabInstance Create() => new Instance(); 8 | 9 | public enum Priority 10 | { 11 | Low = 0, 12 | Normal = 1, 13 | High = 2, 14 | Critical = 3, 15 | } 16 | 17 | [Flags] 18 | public enum FilePermissions 19 | { 20 | None = 0, 21 | Read = 1, 22 | Write = 2, 23 | Execute = 4, 24 | Delete = 8, 25 | ReadWrite = Read | Write, 26 | FullControl = Read | Write | Execute | Delete, 27 | } 28 | 29 | private class Instance : TabInstance 30 | { 31 | public override void Load(Call call, TabModel model) 32 | { 33 | model.Items = new List 34 | { 35 | new("Regular Enum - Low", Priority.Low), 36 | new("Regular Enum - High", Priority.High), 37 | new("Flags - None", FilePermissions.None), 38 | new("Flags - Read", FilePermissions.Read), 39 | new("Flags - ReadWrite", FilePermissions.ReadWrite), 40 | new("Flags - FullControl", FilePermissions.FullControl), 41 | }; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Tasks/TaskInstanceCollection.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Collections; 2 | 3 | namespace SideScroll.Tasks; 4 | 5 | /// 6 | /// A collection of task instances that automatically limits the number of stored tasks by removing oldest entries when the maximum is exceeded 7 | /// 8 | public class TaskInstanceCollection : ItemCollection 9 | { 10 | public int MaxTasks { get; set; } = 10; 11 | 12 | /// 13 | /// Initializes a new empty task instance collection 14 | /// 15 | public TaskInstanceCollection() { } 16 | 17 | /// 18 | /// Initializes a new task instance collection with the specified items 19 | /// 20 | public TaskInstanceCollection(IEnumerable enumerable) : 21 | base(enumerable) 22 | { 23 | } 24 | 25 | /// 26 | /// Adds a task instance and automatically removes the oldest entry if the collection exceeds MaxTasks 27 | /// 28 | public new void Add(TaskInstance taskInstance) 29 | { 30 | base.Add(taskInstance); 31 | 32 | if (Count > MaxTasks) 33 | { 34 | RemoveAt(0); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/ScreenCapture/ScreenCaptureToolbar.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Controls.Toolbar; 2 | using SideScroll.Resources; 3 | using SideScroll.Utilities; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace SideScroll.Avalonia.Controls.ScreenCapture; 7 | 8 | public class ScreenCaptureToolbar : TabControlToolbar 9 | { 10 | protected override Type StyleKeyOverride => typeof(TabControlToolbar); 11 | 12 | public ToolbarButton? ButtonCopyClipboard { get; } 13 | public ToolbarButton ButtonSave { get; } 14 | public ToolbarButton ButtonOpenFolder { get; } 15 | public ToolbarButton ButtonClose { get; } 16 | 17 | public ScreenCaptureToolbar() 18 | { 19 | OSPlatform platform = ProcessUtils.GetOSPlatform(); 20 | if (platform != OSPlatform.Linux) 21 | { 22 | ButtonCopyClipboard = AddButton("Copy to Clipboard", Icons.Svg.Copy); 23 | } 24 | ButtonSave = AddButton("Save", Icons.Svg.Save); 25 | 26 | AddSeparator(); 27 | ButtonOpenFolder = AddButton("Open Folder", Icons.Svg.OpenFolder); 28 | 29 | AddSeparator(); 30 | ButtonClose = AddButton("Close Snapshot", Icons.Svg.Delete); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/BlankDocument.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Gary Hertel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/Charts/TabChartSizes.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Charts.LiveCharts; 2 | using SideScroll.Charts; 3 | using SideScroll.Collections; 4 | using SideScroll.Extensions; 5 | using SideScroll.Tabs; 6 | using SideScroll.Tabs.Samples.Charts; 7 | 8 | namespace SideScroll.Avalonia.Samples.Charts; 9 | 10 | public class TabChartSizes : ITab 11 | { 12 | public TabInstance Create() => new Instance(); 13 | 14 | private class Instance : TabInstance 15 | { 16 | public override void LoadUI(Call call, TabModel model) 17 | { 18 | model.MinDesiredWidth = 1000; 19 | 20 | DateTime dateTime = DateTime.Now.Trim(); 21 | var series = ChartSamples.CreateTimeSeries(dateTime, TimeSpan.FromHours(6)); 22 | 23 | var chartView = new ChartView 24 | { 25 | LegendPosition = ChartLegendPosition.Hidden, 26 | ShowTimeTracker = true, 27 | }; 28 | chartView.AddSeries("Count", series, seriesType: SeriesType.Average); 29 | 30 | var liveChart = new TabLiveChart(this, chartView) 31 | { 32 | Height = 80, 33 | }; 34 | liveChart.Chart.MinHeight = 80; 35 | model.AddObject(liveChart); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleGridDictionary.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.DataGrid; 2 | 3 | public class TabSampleGridDictionary : ITab 4 | { 5 | public TabInstance Create() => new Instance(); 6 | 7 | private class Instance : TabInstance 8 | { 9 | private Dictionary _items = []; 10 | 11 | public override void Load(Call call, TabModel model) 12 | { 13 | _items = []; 14 | AddEntries(); 15 | model.AddData(_items); 16 | 17 | // Dictionary not observable 18 | /*model.Actions = new List 19 | { 20 | new TaskDelegate("Add Entries", AddEntries), 21 | };*/ 22 | } 23 | 24 | private void AddEntries() 25 | { 26 | for (int i = 0; i < 20; i++) 27 | { 28 | int index = _items.Count; 29 | TestItem testItem = new() 30 | { 31 | Name = index.ToString(), 32 | Value = index * 100, 33 | }; 34 | _items.Add(testItem.Name, testItem); 35 | } 36 | } 37 | } 38 | 39 | private class TestItem 40 | { 41 | public string? Name { get; set; } 42 | public int Value { get; set; } 43 | 44 | public override string? ToString() => Name; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Samples/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Loading/TabSampleLoadAsync.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Resources; 2 | using SideScroll.Tabs.Toolbar; 3 | 4 | namespace SideScroll.Tabs.Samples.Loading; 5 | 6 | public class TabSampleLoadAsync : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Toolbar : TabToolbar 11 | { 12 | public ToolButton ButtonRefresh { get; } = new("Refresh", Icons.Svg.Refresh); 13 | } 14 | 15 | private class Instance : TabInstance, ITabAsync 16 | { 17 | private const int DelayMs = 5000; 18 | 19 | public Instance() 20 | { 21 | LoadingMessage = "Loading ALL the things!"; 22 | } 23 | 24 | public async Task LoadAsync(Call call, TabModel model) 25 | { 26 | Toolbar toolbar = new(); 27 | toolbar.ButtonRefresh.Action = ReloadInstance; 28 | model.AddObject(toolbar); 29 | 30 | call.Log.Add("Sleeping", new Tag("Milliseconds", DelayMs)); 31 | 32 | await Task.Delay(DelayMs); 33 | 34 | model.AddObject("Finished"); 35 | 36 | model.Items = new List 37 | { 38 | 1, 2, 3 39 | }; 40 | } 41 | 42 | private void ReloadInstance(Call call) 43 | { 44 | Reload(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/Copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Console/Console.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Logs; 2 | using SideScroll.Tabs; 3 | using SideScroll.Tabs.Settings; 4 | 5 | namespace SideScroll.Demo.Console; 6 | 7 | public class Console 8 | { 9 | private readonly Call _call; 10 | private readonly LogWriterConsole _logWriterConsole; 11 | private readonly LogWriterText _logWriterText; 12 | 13 | private static void Main(string[] args) 14 | { 15 | var console = new Console(); 16 | } 17 | 18 | public Console() 19 | { 20 | // setup 21 | var project = Project.Load(Settings); 22 | _call = new Call(GetType().Name); 23 | _logWriterConsole = new LogWriterConsole(_call.Log); 24 | _logWriterText = new LogWriterText(_call.Log, project.Data.App.GetGroupPath(typeof(Console)) + "/Logs/Main"); 25 | 26 | //TestLogWriter(); 27 | } 28 | 29 | public static ProjectSettings Settings => new() 30 | { 31 | Name = "SideScroll", 32 | LinkType = "sidescroll", 33 | Version = new Version(1, 0), 34 | DataVersion = new Version(1, 0), 35 | }; 36 | 37 | void TestLogWriter() 38 | { 39 | _call.Log.Add("test"); 40 | } 41 | } 42 | /* 43 | This should probably get turned into a library if it ever gets used 44 | */ 45 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/Toolbar/ToolbarTextBox.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Layout; 3 | using Avalonia.Media; 4 | using SideScroll.Avalonia.Themes; 5 | 6 | namespace SideScroll.Avalonia.Controls.Toolbar; 7 | 8 | public class ToolbarTextBox : TextBox 9 | { 10 | protected override Type StyleKeyOverride => typeof(TextBox); 11 | 12 | public ToolbarTextBox(string text = "") 13 | { 14 | Text = text; 15 | TextWrapping = TextWrapping.NoWrap; 16 | VerticalAlignment = VerticalAlignment.Center; 17 | 18 | LoadTheme(); 19 | 20 | ActualThemeVariantChanged += (_, _) => LoadTheme(); 21 | } 22 | 23 | private void LoadTheme() 24 | { 25 | Background = SideScrollTheme.ToolbarTextBackground; 26 | Foreground = SideScrollTheme.ToolbarTextForeground; 27 | CaretBrush = SideScrollTheme.ToolbarTextCaret; 28 | 29 | // Fluent 30 | Resources["TextControlBackgroundPointerOver"] = Background; 31 | Resources["TextControlBackgroundFocused"] = Background; 32 | Resources["TextControlPlaceholderForegroundFocused"] = SideScrollTheme.ToolbarTextForeground; 33 | Resources["TextControlPlaceholderForegroundPointerOver"] = SideScrollTheme.ToolbarTextForeground; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleGridColumnOrdering.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Tabs.Samples.DataGrid; 2 | 3 | // Tests the column order matches for the different types 4 | public class TabSampleGridColumnOrdering : ITab 5 | { 6 | public TabInstance Create() => new Instance(); 7 | 8 | private class Instance : TabInstance 9 | { 10 | public override void Load(Call call, TabModel model) 11 | { 12 | List items = []; 13 | for (int i = 0; i < 2; i++) 14 | { 15 | TestChild item = new(); 16 | items.Add(item); 17 | } 18 | 19 | model.Items = items; 20 | } 21 | } 22 | 23 | private abstract class TestParent 24 | { 25 | public string Field = "1"; 26 | public string Original { get; set; } = "2"; 27 | public virtual string Virtual { get; set; } = "3"; 28 | public virtual string Overriden { get; set; } = "4"; 29 | public abstract string Abstract { get; } 30 | 31 | public override string ToString() => Original; 32 | } 33 | 34 | private class TestChild : TestParent 35 | { 36 | public override string Overriden { get; set; } = "5"; 37 | public override string Abstract => "6"; 38 | 39 | public override string ToString() => Original; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Eraser.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts.LiveCharts/TabLiveChartLegend.cs: -------------------------------------------------------------------------------- 1 | using LiveChartsCore; 2 | using LiveChartsCore.SkiaSharpView.Avalonia; 3 | 4 | namespace SideScroll.Avalonia.Charts.LiveCharts; 5 | 6 | public class TabLiveChartLegend(TabLiveChart liveChart) 7 | : TabChartLegend(liveChart) 8 | { 9 | public TabLiveChart LiveChart => liveChart; 10 | public CartesianChart Chart => LiveChart.Chart; 11 | 12 | protected override TabChartLegendItem AddSeries(ChartSeries chartSeries) 13 | { 14 | var legendItem = new TabLiveChartLegendItem(this, chartSeries); 15 | legendItem.OnVisibilityChanged += LegendItem_VisibilityChanged; 16 | legendItem.PointerPressed += (s, e) => 17 | { 18 | if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) 19 | { 20 | SelectLegendItem(legendItem); 21 | } 22 | }; 23 | LegendItems.Add(legendItem); 24 | 25 | if (chartSeries.LineSeries.Name is string name) 26 | { 27 | IdxLegendItems.Add(name, legendItem); 28 | } 29 | return legendItem; 30 | } 31 | 32 | public override void UpdateHighlight(bool showFaded) 33 | { 34 | base.UpdateHighlight(showFaded); 35 | 36 | LiveChart.UpdateAxis(); 37 | LiveChart.InvalidateChart(); 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Forms/TabSampleItem.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Serialize.DataRepos; 3 | 4 | namespace SideScroll.Tabs.Samples.Forms; 5 | 6 | public class TabSampleItem : ITab, IDataView 7 | { 8 | [DataValue] 9 | public SampleItem? TestItem; 10 | 11 | //[ButtonColumn("-")] 12 | public event EventHandler? OnDelete; 13 | 14 | [ButtonColumn("-")] 15 | public void Delete() 16 | { 17 | OnDelete?.Invoke(this, EventArgs.Empty); 18 | } 19 | 20 | [DataKey] 21 | public string? Name => TestItem?.Name; 22 | 23 | public bool? Boolean => TestItem?.Boolean; 24 | 25 | public int? Amount => TestItem?.Amount; 26 | 27 | public TabSampleItem() { } 28 | 29 | public TabSampleItem(SampleItem testItem) 30 | { 31 | TestItem = testItem; 32 | } 33 | 34 | public void Load(object sender, object obj, object?[] tabParams) 35 | { 36 | TestItem = (SampleItem)obj; 37 | } 38 | 39 | public override string? ToString() => Name; 40 | 41 | public TabInstance Create() => new Instance(this); 42 | 43 | private class Instance(TabSampleItem tab) : TabInstance 44 | { 45 | public override void Load(Call call, TabModel model) 46 | { 47 | model.AddData(tab.TestItem); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Lists/ListByte.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Lists; 4 | 5 | public class ListByte(int index, byte b) 6 | { 7 | public static int MaxBytes { get; set; } = 100_000; 8 | 9 | [StyleValue] 10 | public int Index => index; 11 | 12 | public byte Byte => b; 13 | public string Hex { get; init; } = BitConverter.ToString([b]); 14 | public char Char { get; init; } = Convert.ToChar(b); 15 | public string Bits { get; init; } = Convert.ToString(b, 2).PadLeft(8, '0'); 16 | 17 | public override string ToString() => Index.ToString(); 18 | 19 | public static List Create(IEnumerable bytes) 20 | { 21 | int i = 0; 22 | return bytes 23 | .Take(MaxBytes) 24 | .Select(b => new ListByte(i++, b)) 25 | .ToList(); 26 | } 27 | 28 | public static List Load(string path) 29 | { 30 | var fileInfo = new FileInfo(path); 31 | if (fileInfo.Length > MaxBytes) 32 | { 33 | using FileStream fileStream = File.OpenRead(path); 34 | 35 | var buffer = new byte[MaxBytes]; 36 | fileStream.ReadExactly(buffer, 0, buffer.Length); 37 | return Create(buffer); 38 | } 39 | else 40 | { 41 | return Create(File.ReadAllBytes(path)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Charts/TabSampleChartSeriesCount.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Charts; 2 | using SideScroll.Collections; 3 | using SideScroll.Extensions; 4 | using SideScroll.Tabs.Lists; 5 | using SideScroll.Time; 6 | 7 | namespace SideScroll.Tabs.Samples.Charts; 8 | 9 | public class TabSampleChartSeriesCount : ITab 10 | { 11 | public TabInstance Create() => new Instance(); 12 | 13 | private class Instance : TabInstance 14 | { 15 | public override void Load(Call call, TabModel model) 16 | { 17 | model.Items = new List 18 | { 19 | new(10, TabModel.Create("10", CreateChartView(10))), 20 | new(25, TabModel.Create("25", CreateChartView(25))), 21 | }; 22 | } 23 | 24 | private static ChartView CreateChartView(int seriesCount) 25 | { 26 | DateTime endTime = TimeZoneView.Now.Trim(TimeSpan.TicksPerHour).AddHours(-12); 27 | 28 | var chartView = new ChartView($"{seriesCount} Chart Series") 29 | { 30 | ShowTimeTracker = true, 31 | }; 32 | 33 | for (int i = 0; i < seriesCount; i++) 34 | { 35 | chartView.AddSeries($"Series {i}", ChartSamples.CreateTimeSeries(endTime, TimeSpan.FromDays(1), 12), seriesType: SeriesType.Average); 36 | } 37 | return chartView; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts.LiveCharts/SideScroll.Avalonia.Charts.LiveCharts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | MSBuild:Compile 14 | 15 | 16 | MSBuild:Compile 17 | 18 | 19 | 20 | 21 | 22 | True 23 | \ 24 | 25 | 26 | True 27 | \ 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleHeaders.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Collections; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Samples.DataGrid; 5 | 6 | public class TabSampleHeaders : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | public override void Load(Call call, TabModel model) 13 | { 14 | model.Items = new List 15 | { 16 | new("Show Headers", BuildShowHeaders()), 17 | new("Hide Headers", BuildHideHeaders()), 18 | }; 19 | } 20 | 21 | private static ItemCollection BuildShowHeaders() 22 | { 23 | ItemCollection items = 24 | [ 25 | "Item 1", 26 | "Item 2", 27 | "Item 3", 28 | ]; 29 | items.ShowHeader = true; 30 | return items; 31 | } 32 | 33 | private static ItemCollection BuildHideHeaders() 34 | { 35 | ItemCollection items = []; 36 | for (int i = 0; i < 10; i++) 37 | { 38 | items.Add(new TestItem($"Test {i}", i)); 39 | } 40 | items.ShowHeader = false; 41 | return items; 42 | } 43 | 44 | private class TestItem(string id, int value) 45 | { 46 | public string Id => id; 47 | public int Value => value; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/CallTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace SideScroll.Tests; 4 | 5 | [Category("Call")] 6 | public class CallTests : BaseTest 7 | { 8 | private readonly List _input = [null, 0, 1]; 9 | 10 | [OneTimeSetUp] 11 | public void BaseSetup() 12 | { 13 | Initialize("Call"); 14 | } 15 | 16 | private async Task EchoAsync(Call call, int? index) 17 | { 18 | await Task.Delay(1); 19 | return index; 20 | } 21 | 22 | [Test] 23 | public async Task FirstNonNullAsync() 24 | { 25 | var result = await Call.FirstNonNullAsync(EchoAsync, _input); 26 | 27 | Assert.That(result, Is.EqualTo(0)); 28 | } 29 | 30 | [Test] 31 | public async Task SelectNonNullAsync() 32 | { 33 | var result = await Call.SelectNonNullAsync(EchoAsync, _input); 34 | 35 | Assert.That(result, Has.Exactly(2).Items); 36 | Assert.That(result, Is.EquivalentTo([0, 1])); 37 | } 38 | 39 | [Test] 40 | public async Task RunAsync() 41 | { 42 | var result = await Call.RunAsync(EchoAsync, _input); 43 | 44 | Assert.That(result, Has.Exactly(3).Items); 45 | Assert.That(result.Keys, Is.EquivalentTo(_input)); 46 | Assert.That(result.Values, Is.EquivalentTo(_input)); 47 | Assert.That(result.NonNullValues, Is.EquivalentTo([0, 1])); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/List1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Tests/SideScroll.Serialize.Tests/SerializeItemCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Collections; 3 | using SideScroll.Serialize.Atlas; 4 | 5 | namespace SideScroll.Serialize.Tests; 6 | 7 | [Category("SerializeItemCollection")] 8 | public class SerializeItemCollectionTests : SerializeBaseTest 9 | { 10 | private SerializerMemory _serializer = new SerializerMemoryAtlas(); 11 | 12 | [OneTimeSetUp] 13 | public void BaseSetup() 14 | { 15 | Initialize("SerializeItemCollection"); 16 | } 17 | 18 | [SetUp] 19 | public void Setup() 20 | { 21 | _serializer = new SerializerMemoryAtlas(); 22 | } 23 | 24 | [Test, Description("Serialize ItemCollection")] 25 | public void SerializeItemCollection() 26 | { 27 | var input = new TestBindingList(); 28 | 29 | _serializer.Save(Call, input); 30 | TestBindingList output = _serializer.Load(Call); 31 | 32 | //Assert.AreEqual(input.uintTest, output.uintTest); 33 | } 34 | 35 | public class ReferencedClass 36 | { 37 | public int IntField = 1; 38 | } 39 | 40 | public class TestBindingList 41 | { 42 | public ItemCollection IntList = [1]; 43 | public ItemCollection StringList = ["abc"]; 44 | public ItemCollection RefList = [new ReferencedClass()]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll/CallTimer.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Timers; 3 | 4 | namespace SideScroll; 5 | 6 | public class CallTimer : Call, IDisposable 7 | { 8 | private readonly Stopwatch _stopwatch = new(); 9 | private readonly System.Timers.Timer _timer = new(); 10 | 11 | public bool IsTask { get; set; } 12 | 13 | public long ElapsedMilliseconds => _stopwatch.ElapsedMilliseconds; 14 | 15 | public CallTimer() 16 | { 17 | _stopwatch.Start(); 18 | 19 | _timer.Interval = 1000.0; 20 | _timer.Elapsed += Timer_Elapsed; 21 | _timer.Start(); 22 | } 23 | 24 | public void Stop() 25 | { 26 | _stopwatch.Stop(); 27 | 28 | _timer.Stop(); 29 | _timer.Elapsed -= Timer_Elapsed; 30 | 31 | UpdateDuration(); 32 | 33 | if (TaskInstance != null && IsTask) 34 | { 35 | TaskInstance.SetFinished(); 36 | } 37 | else 38 | { 39 | Log?.Add("Finished", new Tag("Duration", _stopwatch.Elapsed)); 40 | } 41 | } 42 | 43 | private void Timer_Elapsed(object? sender, ElapsedEventArgs e) 44 | { 45 | UpdateDuration(); 46 | } 47 | 48 | private void UpdateDuration() 49 | { 50 | if (Log != null) 51 | { 52 | Log.Duration = _stopwatch.Elapsed; 53 | } 54 | } 55 | 56 | public void Dispose() 57 | { 58 | Stop(); 59 | 60 | _timer.Dispose(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/FormatTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Extensions; 3 | 4 | namespace SideScroll.Tests; 5 | 6 | [Category("Formatting")] 7 | public class FormatTests : BaseTest 8 | { 9 | [OneTimeSetUp] 10 | public void BaseSetup() 11 | { 12 | Initialize("Format"); 13 | } 14 | 15 | [Test] 16 | public void TimeSpanFormattedShort() 17 | { 18 | Assert.That(TimeSpan.FromSeconds(1.5).FormattedShort(), Is.EqualTo("1.5")); 19 | Assert.That(new TimeSpan(0, 0, 1, 23).FormattedShort(), Is.EqualTo("1:23")); 20 | Assert.That(new TimeSpan(0, 1, 2, 3).FormattedShort(), Is.EqualTo("1:02:03")); 21 | Assert.That(new TimeSpan(1, 2, 3, 4).FormattedShort(), Is.EqualTo("1:2:03:04")); 22 | Assert.That(new TimeSpan(1, 2, 3, 4, 125).FormattedShort(), Is.EqualTo("1:2:03:04.125")); 23 | Assert.That(new TimeSpan(1, 2, 3, 4, 5).FormattedShort(), Is.EqualTo("1:2:03:04.005")); 24 | } 25 | 26 | [Test] 27 | public void DoubleFormattedShortDecimal() 28 | { 29 | Assert.That(1.123.FormattedShortDecimal(), Is.EqualTo("1.123")); 30 | Assert.That(0.999_998.FormattedShortDecimal(6), Is.EqualTo("0.999998")); 31 | Assert.That(0.000_002.FormattedShortDecimal(6), Is.EqualTo("0.000002")); 32 | Assert.That(9_999.998.FormattedShortDecimal(6), Is.EqualTo("9.999998 K")); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Serialize/Atlas/TypeRepos/TypeRepoString.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Serialize.Atlas.Schema; 2 | 3 | namespace SideScroll.Serialize.Atlas.TypeRepos; 4 | 5 | public class TypeRepoString(Serializer serializer, TypeSchema typeSchema) : TypeRepo(serializer, typeSchema) 6 | { 7 | public class Creator : IRepoCreator 8 | { 9 | public TypeRepo? TryCreateRepo(Serializer serializer, TypeSchema typeSchema) 10 | { 11 | if (typeSchema.Type == typeof(string)) 12 | { 13 | return new TypeRepoString(serializer, typeSchema); 14 | } 15 | return null; 16 | } 17 | } 18 | 19 | public override void SaveObject(BinaryWriter writer, object obj) 20 | { 21 | writer.Write((string)obj); 22 | } 23 | 24 | protected override object? CreateObject(int objectIndex) 25 | { 26 | long position = Reader!.BaseStream.Position; 27 | Reader.BaseStream.Position = ObjectOffsets![objectIndex]; 28 | 29 | object obj = Reader.ReadString(); 30 | Reader.BaseStream.Position = position; 31 | 32 | ObjectsLoaded[objectIndex] = obj; // must assign before loading any more refs 33 | return obj; 34 | } 35 | 36 | public override void Clone(object source, object dest) 37 | { 38 | // assigning won't do anything since it's not a ref 39 | throw new SerializerException("Not cloneable"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Serialize/SideScroll.Serialize.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Library 5 | SideScroll.Serialize 6 | SideScroll.Serialize 7 | 8 | 9 | 10 | AnyCPU 11 | 12 | 13 | 14 | AnyCPU 15 | 16 | 17 | 18 | 19 | True 20 | \ 21 | 22 | 23 | True 24 | \ 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Objects/TabSampleObjects.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Samples.Objects; 4 | 5 | [ListItem] 6 | public class TabSampleObjects 7 | { 8 | public static TabSampleObjectMembers ObjectMembers => new(); 9 | public static TabSampleObjectProperties ObjectProperties => new(); 10 | public static Tag[] Tags => [new Tag("abc", 1.1)]; 11 | public static TabSampleSubClassProperty SubclassProperty => new(); 12 | public static ValueSub Subclass => new(); 13 | public static EnumTest Enum => new(); 14 | public static TimeSpan TimeSpan => new(1, 2, 3); 15 | public static TabSampleJson Json => new(); 16 | public static Uri Uri => new("https://wikipedia.org"); 17 | public static TabSampleDebug Debug => new(); 18 | } 19 | 20 | public class MyClass 21 | { 22 | public string Description { get; set; } = "Really long value that keeps going on and on and on, or at least for a really long time and we see some wordwrap"; 23 | 24 | public override string ToString() => Description; 25 | } 26 | 27 | public enum EnumTest 28 | { 29 | One = 1, 30 | Two = 2, 31 | Four = 4, 32 | Eight = 8, 33 | } 34 | 35 | public class ValueBase 36 | { 37 | public int Value = 1; 38 | } 39 | 40 | public class ValueSub : ValueBase 41 | { 42 | public new int Value = 2; 43 | } 44 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Tabs/TabText.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Avalonia.Controls; 2 | using SideScroll.Avalonia.Controls.TextEditor; 3 | using SideScroll.Avalonia.Utilities; 4 | using SideScroll.Resources; 5 | using SideScroll.Tabs; 6 | using SideScroll.Tabs.Toolbar; 7 | 8 | namespace SideScroll.Avalonia.Tabs; 9 | 10 | public class TabText(string text) : ITab 11 | { 12 | public string Text => text; 13 | 14 | public TabInstance Create() => new Instance(this); 15 | 16 | public class Toolbar : TabToolbar 17 | { 18 | public ToolButton ButtonCopy { get; } = new("Copy", Icons.Svg.Copy); 19 | } 20 | 21 | private class Instance(TabText tab) : TabInstance 22 | { 23 | public override void LoadUI(Call call, TabModel model) 24 | { 25 | model.MinDesiredWidth = 100; 26 | model.MaxDesiredWidth = 1000; 27 | 28 | Toolbar toolbar = new(); 29 | toolbar.ButtonCopy.ActionAsync = CopyAsync; 30 | model.AddObject(toolbar); 31 | 32 | var tabAvaloniaEdit = new TabAvaloniaEdit(); 33 | tabAvaloniaEdit.SetFormatted(tab.Text); 34 | model.AddObject(tabAvaloniaEdit, true); 35 | } 36 | 37 | private async Task CopyAsync(Call call) 38 | { 39 | await ClipboardUtils.SetTextAsync(BaseWindow.Instance, tab.Text); 40 | 41 | call.TaskInstance!.ShowMessage("Copied to Clipboard"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Report.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleDataGrid.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | 3 | namespace SideScroll.Tabs.Samples.DataGrid; 4 | 5 | [ListItem] 6 | public class TabSampleDataGrid 7 | { 8 | public static TabSampleGridColumnTypes ColumnTypes => new(); 9 | public static TabSampleGridColumnSizing ColumnSizing => new(); 10 | public static TabSampleGridColumnOrdering ColumnOrdering => new(); 11 | public static TabSampleGridColumnCount ColumnCount => new(); 12 | public static TabSampleWideColumns WideColumns => new(); 13 | public static TabSampleGridCollectionSize CollectionSize => new(); 14 | public static TabSampleGridHashSet Enumerable => new(); 15 | public static TabSampleGridDictionary Dictionary => new(); 16 | public static TabSampleArray Array => new(); 17 | public static TabSampleNullableArray NullableArray => new(); 18 | public static TabSampleBytes Bytes => new(); 19 | public static TabSampleEnums Enums => new(); 20 | public static TabSampleGridDataTable DataTable => new(); 21 | public static TabSampleInstanceListItems InstanceListItems => new(); 22 | public static TabSampleFilter Filter => new(); 23 | public static TabSampleGridUpdating Updating => new(); 24 | public static TabSampleHeaders Headers => new(); 25 | // public static TabSampleGridEditing Editing => new(); 26 | } 27 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Forms/TabSampleFormUpdating.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Resources; 2 | using SideScroll.Tabs.Toolbar; 3 | 4 | namespace SideScroll.Tabs.Samples.Forms; 5 | 6 | public class TabSampleFormUpdating : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Toolbar : TabToolbar 11 | { 12 | public ToolButton ButtonRandomize { get; } = new ToolButton("Randomize", Icons.Svg.Refresh); 13 | } 14 | 15 | private class Instance : TabInstance 16 | { 17 | private readonly SynchronizationContext _context = SynchronizationContext.Current ?? new(); 18 | 19 | private SampleItemDataBinding? _sampleItem; 20 | private readonly Random _random = new(); 21 | 22 | public override void Load(Call call, TabModel model) 23 | { 24 | _sampleItem = new SampleItemDataBinding(_context); 25 | Randomize(call); 26 | model.AddForm(_sampleItem!); 27 | 28 | Toolbar toolbar = new(); 29 | toolbar.ButtonRandomize.Action = Randomize; 30 | model.AddObject(toolbar); 31 | } 32 | 33 | private void Randomize(Call call) 34 | { 35 | while (true) 36 | { 37 | int value = _random.Next() % 3; 38 | 39 | if (value.ToString() == _sampleItem?.Value) continue; 40 | 41 | _sampleItem!.Value = value.ToString(); 42 | break; 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Network/Http/HttpClientManager.cs: -------------------------------------------------------------------------------- 1 | namespace SideScroll.Network.Http; 2 | 3 | public record HttpClientConfig(string? Accept = null, TimeSpan? Timeout = null) 4 | { 5 | public bool IsDefault => Accept == null && Timeout == null; 6 | } 7 | 8 | // todo: Figure out a way to reuse default client and inject in request 9 | // alternative: HttpClientFactory 10 | public static class HttpClientManager 11 | { 12 | private static readonly HttpClientHandler _handler = new() 13 | { 14 | AllowAutoRedirect = false, 15 | }; 16 | 17 | private static readonly HttpClient _defaultClient = new(_handler); 18 | 19 | private static readonly Dictionary _clients = []; 20 | 21 | public static HttpClient GetClient(HttpClientConfig config) 22 | { 23 | if (config.IsDefault) return _defaultClient; 24 | 25 | lock (_clients) 26 | { 27 | string id = config.ToString(); 28 | if (_clients.TryGetValue(id, out HttpClient? client)) return client; 29 | 30 | client = new HttpClient(_handler); 31 | 32 | if (config.Accept != null) 33 | { 34 | client.DefaultRequestHeaders.Add("Accept", config.Accept); 35 | } 36 | 37 | if (config.Timeout is TimeSpan timeout) 38 | { 39 | client.Timeout = timeout; 40 | } 41 | 42 | _clients[id] = client; 43 | return client; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Enter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Serialize/Atlas/TypeRepos/TypeRepoUnknown.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Serialize.Atlas.Schema; 2 | 3 | namespace SideScroll.Serialize.Atlas.TypeRepos; 4 | 5 | public class TypeRepoUnknown(Serializer serializer, TypeSchema typeSchema) : TypeRepo(serializer, typeSchema) 6 | { 7 | public class Creator : IRepoCreator 8 | { 9 | public TypeRepo? TryCreateRepo(Serializer serializer, TypeSchema typeSchema) 10 | { 11 | if (typeSchema.Type == null) 12 | { 13 | return new TypeRepoUnknown(serializer, typeSchema); 14 | } 15 | return null; 16 | } 17 | } 18 | 19 | public class NoConstructorCreator : IRepoCreator 20 | { 21 | public TypeRepo? TryCreateRepo(Serializer serializer, TypeSchema typeSchema) 22 | { 23 | if (!typeSchema.HasConstructor || 24 | (typeSchema.IsPublicOnly && !serializer.PublicOnly)) 25 | { 26 | return new TypeRepoUnknown(serializer, typeSchema); 27 | } 28 | return null; 29 | } 30 | } 31 | 32 | public override void SaveObject(BinaryWriter writer, object obj) 33 | { 34 | } 35 | 36 | public override void Clone(object source, object dest) 37 | { 38 | // assigning won't do anything since it's not a ref 39 | throw new SerializerException("Not cloneable"); 40 | } 41 | 42 | protected override object? CreateObject(int objectIndex) 43 | { 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/SideScroll.Tabs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SideScroll.Tabs 5 | SideScroll.Tabs 6 | 7 | 8 | 9 | AnyCPU 10 | 11 | 12 | 13 | AnyCPU 14 | 15 | 16 | 17 | 18 | True 19 | \ 20 | 21 | 22 | True 23 | \ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/TabTextArea.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Primitives; 4 | using Avalonia.Layout; 5 | using Avalonia.Media; 6 | using SideScroll.Avalonia.Utilities; 7 | 8 | namespace SideScroll.Avalonia.Controls; 9 | 10 | // ReadOnly string control with wordwrap, scrolling, and clipboard copy 11 | // See TabAvaloniaEdit for an editable version 12 | public class TabTextArea : Border 13 | { 14 | public string Text { get; } 15 | 16 | public TextBlock TextBlock { get; } 17 | 18 | public TabTextArea(string? text = null) 19 | { 20 | Text = text ?? ""; 21 | 22 | MaxWidth = TabForm.ControlMaxWidth; 23 | 24 | TextBlock = new TextBlock 25 | { 26 | HorizontalAlignment = HorizontalAlignment.Stretch, 27 | VerticalAlignment = VerticalAlignment.Stretch, 28 | TextWrapping = TextWrapping.Wrap, 29 | Text = Text, 30 | Margin = new Thickness(10), 31 | }; 32 | AvaloniaUtils.AddContextMenu(TextBlock); 33 | 34 | var scrollViewer = new ScrollViewer 35 | { 36 | Content = TextBlock, 37 | HorizontalAlignment = HorizontalAlignment.Stretch, 38 | VerticalAlignment = VerticalAlignment.Stretch, 39 | HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled, 40 | VerticalScrollBarVisibility = ScrollBarVisibility.Auto, 41 | }; 42 | 43 | Child = scrollViewer; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Docs/Dev/IDEs/VisualStudioCode.md: -------------------------------------------------------------------------------- 1 | # Visual Studio Code 2 | 3 | ## Installing 4 | 5 | * https://code.visualstudio.com/download 6 | - `sudo apt install ./code_1.15.1-1502903936_amd64.deb` 7 | - Install dotnet core 8 | - https://www.microsoft.com/net/core 9 | - After starting, go to extensions (Cubes icon on left) 10 | - Install "C#" by Microsoft 11 | - Reload 12 | - Load Folder 13 | - Try building 14 | - `Ctrl-Shift-B` 15 | - Access Terminal 16 | - `Ctrl + ~` 17 | 18 | ## Update Settings 19 | 20 | * Settings 21 | - Enable Soft Wrap 22 | - View->Toggle Word Wrap 23 | 24 | * Click Debug icon on left 25 | 26 | * Windows 27 | - Get rid of annoying beep in PowerShell (that vscode uses) 28 | - https://superuser.com/questions/1113429/disable-powershell-beep-on-backspace 29 | - make sure to restart after 30 | 31 | 32 | `dotnet restore SideScroll.sln` 33 | 34 | - Files 35 | - Make sure the `launch.preLaunchTask` matches the `tasks.label` 36 | 37 | ## launch.json 38 | 39 | ```json 40 | { 41 | "version": "0.2.0", 42 | "configurations": [ 43 | { 44 | "preLaunchTask": "build", 45 | }, 46 | ] 47 | } 48 | ``` 49 | ## tasks.json 50 | 51 | ```json 52 | { 53 | "version": "2.0.0", 54 | "tasks": [ 55 | { 56 | "label": "build", 57 | } 58 | ] 59 | } 60 | ``` -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Forms/Todo/SampleTodoItem.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Extensions; 3 | using SideScroll.Time; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace SideScroll.Tabs.Samples.Forms.Todo; 7 | 8 | [PublicData] 9 | public class SampleTodoItem 10 | { 11 | [HiddenRow] 12 | public int Id { get; set; } = 1; 13 | 14 | [DataKey, Required, StringLength(100), WordWrap] 15 | public string? Title { get; set; } 16 | 17 | [WordWrap, AcceptsReturn(true)] 18 | public string? Description { get; set; } 19 | 20 | public TodoPriority Priority { get; set; } = TodoPriority.Medium; 21 | 22 | public static string[] Statuses { get; } = TodoStatus.All; 23 | 24 | [BindList(nameof(Statuses)), ColumnIndex(2)] 25 | public string Status { get; set; } 26 | 27 | [Hidden] 28 | public DateTime Created { get; set; } = TimeZoneView.Now.Trim(); 29 | 30 | public SampleTodoItem() 31 | { 32 | Status = Statuses[0]; 33 | } 34 | 35 | public override string? ToString() => Title; 36 | } 37 | 38 | public enum TodoPriority 39 | { 40 | Low, 41 | Medium, 42 | High, 43 | } 44 | 45 | public class TodoStatus 46 | { 47 | public const string New = "New"; 48 | public const string InProgress = "In Progress"; 49 | public const string Completed = "Completed"; 50 | 51 | public static string[] All => [New, InProgress, Completed]; 52 | } 53 | -------------------------------------------------------------------------------- /Tests/SideScroll.Tests/ObjectUtilsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using SideScroll.Utilities; 3 | 4 | namespace SideScroll.Tests; 5 | 6 | [Category("Core")] 7 | public class ObjectUtilsTests : BaseTest 8 | { 9 | [OneTimeSetUp] 10 | public void BaseSetup() 11 | { 12 | Initialize("ObjectUtils"); 13 | } 14 | 15 | [Test] 16 | public void AreEqual() 17 | { 18 | Assert.That(ObjectUtils.AreEqual(1, 1u)); 19 | } 20 | 21 | [Test] 22 | public void ArrayAreEqual() 23 | { 24 | Assert.That(ObjectUtils.AreEqual( 25 | new int[] { 0 }, 26 | new int[] { 0 } 27 | )); 28 | } 29 | 30 | [Test] 31 | public void ArrayAreNotEqual() 32 | { 33 | Assert.That(ObjectUtils.AreEqual( 34 | new int[] { 0 }, 35 | new int[] { 1 } 36 | ), Is.False); 37 | } 38 | 39 | [Test] 40 | public void SubArrayAreEqual() 41 | { 42 | Assert.That(ObjectUtils.AreEqual( 43 | new int[][] { [0, 0] }, 44 | new int[][] { [0, 0] } 45 | )); 46 | } 47 | 48 | [Test] 49 | public void SubArrayAreNotEqual() 50 | { 51 | Assert.That(ObjectUtils.AreEqual( 52 | new int[][] { [0, 0] }, 53 | new int[][] { [0, 1] } 54 | ), Is.False); 55 | } 56 | 57 | [Test] 58 | public void SubArrayAreNotEqualMaxDepth() 59 | { 60 | Assert.Throws(() => ObjectUtils.AreEqual( 61 | new int[][] { [0, 0] }, 62 | new int[][] { [0, 0] }, 63 | 1)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/LeftArrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/TabSamples.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Samples.Actions; 3 | using SideScroll.Tabs.Samples.Charts; 4 | using SideScroll.Tabs.Samples.DataGrid; 5 | using SideScroll.Tabs.Samples.DataRepo; 6 | using SideScroll.Tabs.Samples.Demo; 7 | using SideScroll.Tabs.Samples.Loading; 8 | using SideScroll.Tabs.Samples.Objects; 9 | using SideScroll.Tabs.Samples.Forms; 10 | using SideScroll.Tabs.Tools; 11 | 12 | namespace SideScroll.Tabs.Samples; 13 | 14 | [ListItem] 15 | public class TabSamples 16 | { 17 | public static TabSampleDemo Demo => new(); 18 | 19 | public static TabSampleObjects Objects => new(); 20 | public static TabSampleDataGrid DataGrid => new(); 21 | public static TabSampleForms Forms => new(); 22 | public static TabSampleToolbar Toolbar => new(); 23 | public static TabSampleActions Actions => new(); 24 | public static TabSampleLoading Loading => new(); 25 | public static TabSampleLogs Logs => new(); 26 | 27 | public static TabSampleTextEditor TextEditor => new(); 28 | public static TabSampleCharts Charts => new(); 29 | 30 | public static TabSampleSerializer Serializer => new(); 31 | public static TabSampleDataRepo DataRepos => new(); 32 | public static TabSampleProcess Process => new(); 33 | public static TabSampleBookmarks Bookmarks => new(); 34 | public static TabIcons Icons => new(); 35 | public static TabTools Tools => new(); 36 | } 37 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/TabUtils.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Extensions; 2 | using SideScroll.Tabs.Lists; 3 | using System.Collections; 4 | 5 | namespace SideScroll.Tabs; 6 | 7 | // Overrides default ObjectHasLinks() 8 | public interface IHasLinks 9 | { 10 | bool HasLinks { get; } 11 | } 12 | 13 | public static class TabUtils 14 | { 15 | public static List IgnoreHighlightTypes { get; set; } = []; 16 | 17 | public static bool ObjectHasLinks(object? obj, bool ignoreEmpty = false) 18 | { 19 | if (obj == null) 20 | return false; 21 | 22 | if (obj is IHasLinks hasLinks) 23 | return hasLinks.HasLinks; 24 | 25 | object? value = obj.GetInnerValue(); 26 | if (value == null) 27 | return false; 28 | 29 | if (value is IListItem listItem) 30 | value = listItem.Value; 31 | 32 | Type type = value!.GetType(); 33 | if (type.IsPrimitive || 34 | type.IsEnum || 35 | type == typeof(string) || 36 | type == typeof(decimal) || 37 | type == typeof(DateTime) || 38 | type == typeof(TimeSpan)) 39 | { 40 | return false; 41 | } 42 | 43 | if (ignoreEmpty) 44 | { 45 | if (value is ICollection collection) 46 | { 47 | if (collection.Count == 0) 48 | return false; 49 | } 50 | 51 | foreach (Type ignoreType in IgnoreHighlightTypes) 52 | { 53 | if (ignoreType.IsAssignableFrom(type)) 54 | return false; 55 | } 56 | } 57 | 58 | return true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Toolbar/ToolComboBox.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tasks; 2 | using System.Collections; 3 | 4 | namespace SideScroll.Tabs.Toolbar; 5 | 6 | public interface IToolComboBox 7 | { 8 | string Label { get; } 9 | object? SelectedObject { get; } 10 | 11 | IList GetItems(); 12 | } 13 | 14 | public class ToolComboBox : IToolComboBox 15 | { 16 | public string Label { get; } 17 | public List Items { get; } 18 | public T? SelectedItem { get; set; } 19 | 20 | public object? SelectedObject 21 | { 22 | get => SelectedItem; 23 | set 24 | { 25 | SelectedItem = (T?)value; 26 | 27 | var call = new Call(); 28 | 29 | Action?.Invoke(call); 30 | ActionAsync?.Invoke(call); 31 | } 32 | } 33 | 34 | public CallAction? Action { get; set; } 35 | public CallActionAsync? ActionAsync { get; set; } 36 | 37 | public ToolComboBox(string label, List items, T selectedItem, CallAction? action = null) 38 | { 39 | Label = label; 40 | Items = items; 41 | SelectedItem = selectedItem; 42 | Action = action; 43 | } 44 | 45 | public ToolComboBox(string label, List items, CallActionAsync actionAsync) 46 | { 47 | Label = label; 48 | Items = items; 49 | ActionAsync = actionAsync; 50 | } 51 | 52 | public IList GetItems() 53 | { 54 | if (Items.Contains(SelectedItem!)) 55 | return Items; 56 | 57 | return new List(Items) 58 | { 59 | SelectedItem! 60 | }; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/TabUserSettings.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Resources; 3 | using SideScroll.Serialize; 4 | using SideScroll.Tabs.Settings; 5 | using SideScroll.Tabs.Toolbar; 6 | 7 | namespace SideScroll.Tabs.Tools; 8 | 9 | // See Avalonia version: TabAvaloniaSettings 10 | public class TabUserSettings : ITab 11 | { 12 | public TabInstance Create() => new Instance(); 13 | 14 | public class Toolbar : TabToolbar 15 | { 16 | public ToolButton ButtonReset { get; } = new("Reset", Icons.Svg.Reset); 17 | 18 | [Separator] 19 | public ToolButton ButtonSave { get; } = new("Save", Icons.Svg.Save); 20 | } 21 | 22 | public class Instance : TabInstance 23 | { 24 | public UserSettings? UserSettings { get; protected set; } 25 | 26 | public override void Load(Call call, TabModel model) 27 | { 28 | Toolbar toolbar = new(); 29 | toolbar.ButtonReset.Action = Reset; 30 | toolbar.ButtonSave.Action = Save; 31 | model.AddObject(toolbar); 32 | 33 | UserSettings = Project.UserSettings.DeepClone(call); 34 | model.AddForm(UserSettings); 35 | } 36 | 37 | private void Reset(Call call) 38 | { 39 | Project.UserSettings = Project.ProjectSettings.DefaultUserSettings; 40 | Refresh(); 41 | } 42 | 43 | private void Save(Call call) 44 | { 45 | Data.App.Save(UserSettings!, call); 46 | Project.UserSettings = UserSettings!.DeepClone(call); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/Lists/ListToString.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Collections; 3 | using SideScroll.Utilities; 4 | using System.Collections; 5 | 6 | namespace SideScroll.Tabs.Lists; 7 | 8 | [Skippable] 9 | public class ListToString 10 | { 11 | public static int MaxItems { get; set; } = 200_000; 12 | 13 | [InnerValue, HiddenColumn] 14 | public object Object { get; } 15 | 16 | public string? Value { get; set; } 17 | 18 | [DataKey, HiddenColumn] 19 | public string? DataKey { get; set; } 20 | 21 | [DataValue, HiddenColumn] 22 | public object? DataValue { get; set; } 23 | 24 | public override string? ToString() => Value; 25 | 26 | public ListToString(object obj) 27 | { 28 | Object = obj; 29 | if (obj == null) 30 | return; 31 | 32 | Value = obj.ToString(); 33 | 34 | DataKey = ObjectUtils.GetDataKey(obj); 35 | DataValue = ObjectUtils.GetDataValue(obj); 36 | } 37 | 38 | public static ItemCollection Create(IEnumerable enumerable, int? limit = null) 39 | { 40 | limit ??= MaxItems; 41 | 42 | var list = new ItemCollection(); 43 | if (enumerable is IItemCollection sourceCollection) 44 | { 45 | (list as IItemCollection).LoadSettings(sourceCollection); 46 | } 47 | foreach (object obj in enumerable) 48 | { 49 | list.Add(new ListToString(obj)); 50 | if (list.Count > limit) 51 | break; 52 | } 53 | return list; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/Import.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Utilities/ImageUtils.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Media.Imaging; 3 | 4 | namespace SideScroll.Avalonia.Utilities; 5 | 6 | /// 7 | /// Provides utility methods for loading and processing images in Avalonia 8 | /// 9 | public static class ImageUtils 10 | { 11 | /// 12 | /// Gets or sets the maximum allowed image dimension (width or height) in pixels 13 | /// 14 | public static int MaxImageSize { get; set; } = 10_000; 15 | 16 | /// 17 | /// Loads a bitmap from byte array data 18 | /// 19 | public static Bitmap LoadBitmap(byte[] bytes) 20 | { 21 | var stream = new MemoryStream(bytes); 22 | return new Bitmap(stream); 23 | } 24 | 25 | /// 26 | /// Loads an image from a file path and sets it as the source for an Image control. 27 | /// Validates that image dimensions do not exceed MaxImageSize. 28 | /// 29 | public static Bitmap LoadImage(Image image, string path) 30 | { 31 | byte[] bytes = File.ReadAllBytes(path); 32 | Bitmap bitmap = LoadBitmap(bytes); 33 | if (bitmap.Size.Width > MaxImageSize) throw new Exception($"Image width {bitmap.Size.Width} is above maximum {MaxImageSize}"); 34 | if (bitmap.Size.Height > MaxImageSize) throw new Exception($"Image height {bitmap.Size.Height} is above maximum {MaxImageSize}"); 35 | 36 | image.Source = bitmap; 37 | return bitmap; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs/TabInterfaces.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Settings; 2 | using System.Collections; 3 | 4 | namespace SideScroll.Tabs; 5 | 6 | // Generates an event when the SelectedItems change 7 | public interface ITabSelector 8 | { 9 | IList? SelectedItems { get; } 10 | 11 | event EventHandler? OnSelectionChanged; 12 | } 13 | 14 | public class TabSelectionChangedEventArgs(bool recreate = false) : EventArgs 15 | { 16 | public bool Recreate => recreate; 17 | } 18 | 19 | // For CustomTabControls 20 | public interface ITabItemSelector 21 | { 22 | IList SelectedItems { get; set; } 23 | } 24 | 25 | // TabInstance or Controls can specify this to create child controls dynamically 26 | public interface ITabCreator 27 | { 28 | object CreateControl(object value, out string? label); 29 | } 30 | 31 | public interface ITabCreatorAsync 32 | { 33 | Task CreateAsync(Call call); 34 | } 35 | 36 | public interface ITabDataControl : IDisposable 37 | { 38 | public TabDataSettings TabDataSettings { get; set; } 39 | 40 | public void LoadSettings(); 41 | } 42 | 43 | public interface ITabDataSelector : ITabDataControl 44 | { 45 | public IList? Items { get; set; } 46 | 47 | public IList SelectedItems { get; set; } 48 | public object? SelectedItem { get; set; } 49 | public HashSet SelectedRows { get; } 50 | 51 | public event EventHandler? OnSelectionChanged; 52 | } 53 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/BaseView.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using SideScroll.Avalonia.Controls.Viewer; 3 | using SideScroll.Avalonia.Tabs; 4 | using SideScroll.Avalonia.Themes; 5 | using SideScroll.Tabs; 6 | using SideScroll.Tabs.Settings; 7 | using SideScroll.Tabs.Tools.FileViewer; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | namespace SideScroll.Avalonia.Controls; 11 | 12 | public class BaseView : UserControl 13 | { 14 | public static BaseView? Instance { get; set; } 15 | 16 | public Project Project { get; protected set; } 17 | 18 | public TabViewer TabViewer { get; protected set; } 19 | 20 | public BaseView(Project project) 21 | { 22 | Instance = this; 23 | 24 | SideScrollInit.Initialize(); 25 | SideScrollTheme.InitializeFonts(); 26 | 27 | TabFile.RegisterType(TabFileImage.DefaultExtensions); 28 | 29 | LoadProject(project); 30 | } 31 | 32 | public BaseView(ProjectSettings settings) : 33 | this(Project.Load(settings)) 34 | { 35 | } 36 | 37 | [MemberNotNull(nameof(Project), nameof(TabViewer))] 38 | private void LoadProject(Project project) 39 | { 40 | project.Initialize(); 41 | Project = project; 42 | 43 | ThemeManager.Initialize(project); 44 | 45 | Background = SideScrollTheme.TabBackground; 46 | 47 | Content = TabViewer = new TabViewer(Project, false); 48 | } 49 | 50 | public virtual void LoadTab(ITab tab) 51 | { 52 | TabViewer.LoadTab(tab); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/DownArrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 4.6.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Tools/FileViewer/TabFileViewer.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Tools.FileViewer; 5 | 6 | [PrivateData] 7 | public class TabFileViewer(SelectFileDelegate? selectFileDelegate = null) : ITab 8 | { 9 | public SelectFileDelegate? SelectFileDelegate => selectFileDelegate; 10 | 11 | public TabInstance Create() => new Instance(this); 12 | 13 | public class Instance(TabFileViewer tab) : TabInstance, ITabAsync 14 | { 15 | public async Task LoadAsync(Call call, TabModel model) 16 | { 17 | var dataRepoFavorites = await FileDataRepos.Favorites.LoadViewAsync(call, Project); 18 | FileSelectorOptions fileSelectorOptions = new() 19 | { 20 | DataRepoFavorites = dataRepoFavorites, 21 | SelectFileDelegate = tab.SelectFileDelegate 22 | }; 23 | 24 | model.Items = new List 25 | { 26 | new("Current", new TabDirectory(Directory.GetCurrentDirectory(), fileSelectorOptions)), 27 | new("Downloads", new TabDirectory(Paths.DownloadPath, fileSelectorOptions)), 28 | new("Drives", new TabDrives(fileSelectorOptions)), 29 | new("Favorites", new TabFileDataRepo(dataRepoFavorites, fileSelectorOptions)), 30 | new("App Data", new TabDirectory(Project.DataSettings.AppDataPath!, fileSelectorOptions)), 31 | new("Local Data", new TabDirectory(Project.DataSettings.LocalDataPath!, fileSelectorOptions)), 32 | }; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/Reset.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Fabric.js 5.3.0 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Controls/Flyouts/ConfirmationFlyout.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Layout; 4 | using Avalonia.Media; 5 | using SideScroll.Avalonia.Extensions; 6 | using SideScroll.Tasks; 7 | 8 | namespace SideScroll.Avalonia.Controls.Flyouts; 9 | 10 | public class ConfirmationFlyout : Flyout 11 | { 12 | public ConfirmationFlyout(Action action, string text, string? confirmText = null, string? cancelText = null) 13 | { 14 | Placement = PlacementMode.Bottom; 15 | Content = new StackPanel 16 | { 17 | Margin = new Thickness(10, 10, 10, 5), 18 | Spacing = 8, 19 | Width = 200, 20 | Children = 21 | { 22 | new TextBlock 23 | { 24 | Text = text, 25 | TextWrapping = TextWrapping.Wrap, 26 | Margin = new Thickness(0, 0, 0, 10) 27 | }, 28 | new StackPanel 29 | { 30 | Orientation = Orientation.Horizontal, 31 | HorizontalAlignment = HorizontalAlignment.Right, 32 | Spacing = 8, 33 | Children = 34 | { 35 | new TabTextButton(cancelText ?? "Cancel") 36 | .Also(button => 37 | { 38 | button.Click += (_, _) => Hide(); 39 | }), 40 | new TabTextButton(confirmText ?? "Confirm", AccentType.Warning) 41 | .Also(button => 42 | { 43 | button.Click += (_, _) => 44 | { 45 | action(); 46 | Hide(); 47 | }; 48 | }) 49 | } 50 | } 51 | } 52 | }; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Objects/TabSampleObjectMembers.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Lists; 3 | 4 | namespace SideScroll.Tabs.Samples.Objects; 5 | 6 | public class TabSampleObjectMembers : ITab 7 | { 8 | public TabInstance Create() => new Instance(); 9 | 10 | private class Instance : TabInstance 11 | { 12 | private readonly SampleMembers _sampleMembers = new(); 13 | 14 | public override void Load(Call call, TabModel model) 15 | { 16 | model.ReloadOnThemeChange = true; 17 | model.Items = ListMember.Create(_sampleMembers); 18 | } 19 | } 20 | } 21 | 22 | public class SampleMembers 23 | { 24 | public static readonly string? StaticStringField = "Static Value"; 25 | public static string? StaticStringProperty { get; set; } = "Static Value"; 26 | 27 | public bool BoolProperty => true; 28 | public bool BoolField = false; 29 | [Item] 30 | public bool BoolMethod() => false; 31 | 32 | public string? StringProperty => "Properties will show in both Rows and Columns by default, and will be cached the first time they're needed"; 33 | public string? StringField = "Fields only show as Rows and not in Columns"; 34 | [Item] 35 | public string StringMethod() => "Method results will be cached the first time they're needed"; 36 | 37 | public MyClass ObjectProperty => new(); 38 | public MyClass ObjectField = new(); 39 | 40 | public MyClass? NullObjectProperty { get; set; } 41 | public MyClass? NullObjectField; 42 | } 43 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataRepo/TabSampleDataRepo.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Tabs.Lists; 2 | using SideScroll.Tabs.Samples.Forms; 3 | using SideScroll.Tabs.Tools.FileViewer; 4 | using SideScroll.Tasks; 5 | 6 | namespace SideScroll.Tabs.Samples.DataRepo; 7 | 8 | public class TabSampleDataRepo : ITab 9 | { 10 | public TabInstance Create() => new Instance(); 11 | 12 | private class Instance : TabInstance 13 | { 14 | public override void Load(Call call, TabModel model) 15 | { 16 | model.AutoSelectSaved = AutoSelectType.NonEmpty; 17 | 18 | model.Items = new List 19 | { 20 | new("Sample Data Repo", new TabSampleDataRepoCollection()), 21 | new("Param Data Repo", new TabSampleFormDataTabs()), 22 | new("Paging", new TabSampleDataRepoPaging()), 23 | new("App Directory", new TabDirectory(Project.Data.App.RepoPath)), 24 | new("Cache Directory", new TabDirectory(Project.Data.Cache.RepoPath)), 25 | }; 26 | 27 | model.Actions = new List 28 | { 29 | new TaskDelegate("Delete Repos", DeleteRepos) 30 | { 31 | Flyout = new ConfirmationFlyoutConfig("Are you sure you want to delete all DataRepos?", "Delete"), 32 | AccentType = AccentType.Warning, 33 | } 34 | }; 35 | } 36 | 37 | private void DeleteRepos(Call call) 38 | { 39 | Project.Data.Cache.DeleteRepo(call); 40 | Project.Data.App.DeleteRepo(call); 41 | Project.Data.Shared.DeleteRepo(call); 42 | 43 | Reload(); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll/Logs/LogTimer.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace SideScroll.Logs; 4 | 5 | public class LogTimer : Log, IDisposable 6 | { 7 | private readonly Stopwatch _stopwatch = new(); 8 | private readonly System.Timers.Timer _timer = new(); 9 | 10 | public LogTimer() { } 11 | 12 | public LogTimer(string text, LogSettings? logSettings, Tag[] tags) : 13 | base(text, logSettings) 14 | { 15 | Tags = tags; 16 | 17 | Add(text, tags); 18 | 19 | InitializeTimer(); 20 | } 21 | 22 | public LogTimer(LogLevel logLevel, string text, LogSettings? logSettings, Tag[] tags) : 23 | base(text, logSettings) 24 | { 25 | Level = logLevel; 26 | Tags = tags; 27 | 28 | Add(logLevel, text, tags); 29 | 30 | InitializeTimer(); 31 | } 32 | 33 | private void InitializeTimer() 34 | { 35 | _stopwatch.Start(); 36 | 37 | _timer.Interval = 1000.0; 38 | _timer.Elapsed += Timer_Elapsed; 39 | _timer.Start(); 40 | } 41 | 42 | private void Timer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) 43 | { 44 | UpdateDuration(); 45 | } 46 | 47 | private void UpdateDuration() 48 | { 49 | Duration = _stopwatch.Elapsed; 50 | 51 | NotifyPropertyChanged(nameof(Duration)); 52 | } 53 | 54 | public void Dispose() 55 | { 56 | _timer.Elapsed -= Timer_Elapsed; 57 | _timer.Stop(); 58 | _timer.Dispose(); 59 | 60 | _stopwatch.Stop(); 61 | 62 | UpdateDuration(); 63 | 64 | Add("Finished", new Tag("Duration", Duration)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/DataGrid/TabSampleGridDataTable.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Extensions; 2 | using SideScroll.Tabs.Lists; 3 | using System.Data; 4 | 5 | namespace SideScroll.Tabs.Samples.DataGrid; 6 | 7 | // DataTable's can be used to show dynamic columns for a DataGrid 8 | // For structured data with known schema's, it's recommended to DataBind directly to the items instead of using a DataTable 9 | public class TabSampleGridDataTable : ITab 10 | { 11 | public TabInstance Create() => new Instance(); 12 | 13 | private class Instance : TabInstance, ITabCreator 14 | { 15 | private DataTable? _dataTable; 16 | 17 | public override void Load(Call call, TabModel model) 18 | { 19 | _dataTable = new(); 20 | _dataTable.Columns.Add(new DataColumn("Id", typeof(int))); 21 | _dataTable.Columns.Add(new DataColumn("Name", typeof(string))); 22 | AddEntries(); 23 | model.AddData(_dataTable); 24 | } 25 | 26 | private void AddEntries() 27 | { 28 | for (int i = 0; i < 10; i++) 29 | { 30 | _dataTable!.Rows.Add(i, $"Name {i}"); 31 | } 32 | } 33 | 34 | public object CreateControl(object value, out string? label) 35 | { 36 | label = value.ToString(); 37 | if (value is DataRowView dataRowView) 38 | { 39 | return dataRowView.Row.ItemArray 40 | .WithIndex() 41 | .Select(x => new ListPair(dataRowView.DataView.Table!.Columns[x.index], x.item)) 42 | .ToList(); 43 | } 44 | 45 | return value.ToString()!; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia/Themes/Controls/TabSplitter.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 25 | 28 | 29 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/StopCircle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/originals/OpenFolder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Programs/SideScroll.Demo.Avalonia.Browser/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | /* HTML styles for the splash screen */ 2 | .avalonia-splash { 3 | position: absolute; 4 | height: 100%; 5 | width: 100%; 6 | background: white; 7 | font-family: 'Outfit', sans-serif; 8 | justify-content: center; 9 | align-items: center; 10 | display: flex; 11 | pointer-events: none; 12 | } 13 | 14 | /* Light theme styles */ 15 | @media (prefers-color-scheme: light) { 16 | .avalonia-splash { 17 | background: white; 18 | } 19 | 20 | .avalonia-splash h2 { 21 | color: #1b2a4e; 22 | } 23 | 24 | .avalonia-splash a { 25 | color: #0D6EFD; 26 | } 27 | } 28 | 29 | @media (prefers-color-scheme: dark) { 30 | .avalonia-splash { 31 | background: #1b2a4e; 32 | } 33 | 34 | .avalonia-splash h2 { 35 | color: white; 36 | } 37 | 38 | .avalonia-splash a { 39 | color: white; 40 | } 41 | } 42 | 43 | .avalonia-splash h2 { 44 | font-weight: 400; 45 | font-size: 1.5rem; 46 | text-align: center; 47 | } 48 | 49 | .avalonia-splash a { 50 | text-decoration: none; 51 | font-size: 2.5rem; 52 | display: block; 53 | text-align: center; 54 | } 55 | 56 | .avalonia-splash .icon { 57 | max-width: 100px; 58 | height: auto; 59 | display: block; 60 | margin: 0 auto; 61 | } 62 | 63 | .avalonia-splash.splash-close { 64 | transition: opacity 100ms, display 100ms; 65 | display: none; 66 | opacity: 0; 67 | } 68 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Tabs.Samples/Objects/TabSampleObjectProperties.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Attributes; 2 | using SideScroll.Tabs.Lists; 3 | using SideScroll.Tasks; 4 | using System.ComponentModel; 5 | 6 | namespace SideScroll.Tabs.Samples.Objects; 7 | 8 | public class TabSampleObjectProperties : ITab 9 | { 10 | public TabInstance Create() => new Instance(); 11 | 12 | private class Instance : TabInstance 13 | { 14 | private readonly PropertyTest _propertyTest = new(); 15 | 16 | public override void Load(Call call, TabModel model) 17 | { 18 | model.ReloadOnThemeChange = true; 19 | model.Items = ListProperty.Create(_propertyTest); 20 | model.Editing = true; 21 | 22 | model.Actions = new List 23 | { 24 | new TaskDelegate("Toggle", Toggle), 25 | }; 26 | } 27 | 28 | private void Toggle(Call call) 29 | { 30 | _propertyTest.Boolean = !_propertyTest.Boolean; 31 | } 32 | } 33 | } 34 | 35 | public class PropertyTest : INotifyPropertyChanged 36 | { 37 | [EditColumn] 38 | public bool Boolean 39 | { 40 | get => _boolean; 41 | set 42 | { 43 | _boolean = value; 44 | NotifyPropertyChangedContext(nameof(Boolean)); 45 | } 46 | } 47 | private bool _boolean; 48 | 49 | public event PropertyChangedEventHandler? PropertyChanged; 50 | 51 | private void NotifyPropertyChangedContext(string propertyName) 52 | { 53 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 54 | } 55 | 56 | public List List { get; set; } = [1, 2, 3]; 57 | } 58 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Resources/Icons/svg/Minimize.svg: -------------------------------------------------------------------------------- 1 | 2 | 15 | 17 | 35 | 40 | 41 | -------------------------------------------------------------------------------- /SideScroll.Desktop.slnf: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "path": "SideScroll.sln", 4 | "projects": [ 5 | "Libraries\\SideScroll\\SideScroll.csproj", 6 | "Libraries\\SideScroll.Avalonia\\SideScroll.Avalonia.csproj", 7 | "Libraries\\SideScroll.Avalonia.Charts\\SideScroll.Avalonia.Charts.csproj", 8 | "Libraries\\SideScroll.Avalonia.Charts.LiveCharts\\SideScroll.Avalonia.Charts.LiveCharts.csproj", 9 | "Libraries\\SideScroll.Avalonia.Samples\\SideScroll.Avalonia.Samples.csproj", 10 | "Libraries\\SideScroll.Avalonia.ScreenCapture\\SideScroll.Avalonia.ScreenCapture.csproj", 11 | "Libraries\\SideScroll.Network\\SideScroll.Network.csproj", 12 | "Libraries\\SideScroll.Resources\\SideScroll.Resources.csproj", 13 | "Libraries\\SideScroll.Serialize\\SideScroll.Serialize.csproj", 14 | "Libraries\\SideScroll.Tabs\\SideScroll.Tabs.csproj", 15 | "Libraries\\SideScroll.Tabs.Samples\\SideScroll.Tabs.Samples.csproj", 16 | "Libraries\\SideScroll.Tabs.Tools\\SideScroll.Tabs.Tools.csproj", 17 | "Programs\\SideScroll.Demo.Avalonia.Desktop\\SideScroll.Demo.Avalonia.Desktop.csproj", 18 | "Programs\\SideScroll.Demo.Console\\SideScroll.Demo.Console.csproj", 19 | "Test\\SideScroll.Avalonia.Test\\SideScroll.Avalonia.Test.csproj", 20 | "Test\\SideScroll.Network.Test\\SideScroll.Network.Test.csproj", 21 | "Test\\SideScroll.Serialize.Test\\SideScroll.Serialize.Test.csproj", 22 | "Test\\SideScroll.Test\\SideScroll.Test.csproj" 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Avalonia.Charts.LiveCharts/TabLiveChartLegendItem.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | using LiveChartsCore; 3 | using LiveChartsCore.SkiaSharpView.Painting; 4 | 5 | namespace SideScroll.Avalonia.Charts.LiveCharts; 6 | 7 | public class TabLiveChartLegendItem(TabLiveChartLegend legend, ChartSeries chartSeries) 8 | : TabChartLegendItem(legend, chartSeries) 9 | { 10 | public TabLiveChartLegend LiveChartLegend => legend; 11 | 12 | public override void UpdateColor(Color color) 13 | { 14 | if (ChartSeries.LineSeries is LiveChartLineSeries lineSeries) 15 | { 16 | var skColor = color.AsSkColor(); 17 | if (lineSeries.Stroke is SolidColorPaint paint && paint.Color == skColor) return; 18 | 19 | lineSeries.Stroke = new SolidColorPaint(skColor) { StrokeThickness = 2 }; 20 | /*if (lineSeries.GeometryStroke != null) 21 | { 22 | lineSeries.GeometryStroke = new SolidColorPaint(skColor) { StrokeThickness = 2 }; 23 | }*/ 24 | if (lineSeries.GeometryFill != null) 25 | { 26 | lineSeries.GeometryFill = new SolidColorPaint(skColor); 27 | } 28 | 29 | var chart = LiveChartLegend.Chart.CoreChart; 30 | 31 | lock (LiveChartLegend.Chart.CoreCanvas.Sync) 32 | { 33 | lineSeries.RemoveOldPaints(chart.View); 34 | } 35 | } 36 | } 37 | 38 | public override void UpdateVisible() 39 | { 40 | bool isVisible = IsSelected || Highlight; 41 | if (isVisible != ChartSeries.LineSeries.IsVisible) 42 | { 43 | ChartSeries.LineSeries.IsVisible = isVisible; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Libraries/SideScroll.Serialize/Atlas/TypeRepos/TypeRepoVersion.cs: -------------------------------------------------------------------------------- 1 | using SideScroll.Serialize.Atlas.Schema; 2 | 3 | namespace SideScroll.Serialize.Atlas.TypeRepos; 4 | 5 | public class TypeRepoVersion(Serializer serializer, TypeSchema typeSchema) : TypeRepo(serializer, typeSchema) 6 | { 7 | public class Creator : IRepoCreator 8 | { 9 | public TypeRepo? TryCreateRepo(Serializer serializer, TypeSchema typeSchema) 10 | { 11 | if (CanAssign(typeSchema.Type)) 12 | { 13 | return new TypeRepoVersion(serializer, typeSchema); 14 | } 15 | return null; 16 | } 17 | } 18 | 19 | public static bool CanAssign(Type? type) 20 | { 21 | return type == typeof(Version); 22 | } 23 | 24 | public override void SaveObject(BinaryWriter writer, object obj) 25 | { 26 | writer.Write(((Version)obj).ToString()); 27 | } 28 | 29 | protected override object? CreateObject(int objectIndex) 30 | { 31 | long position = Reader!.BaseStream.Position; 32 | Reader.BaseStream.Position = ObjectOffsets![objectIndex]; 33 | 34 | string versionText = Reader.ReadString(); 35 | object obj = new Version(versionText); 36 | Reader.BaseStream.Position = position; 37 | 38 | ObjectsLoaded[objectIndex] = obj; // must assign before loading any more refs 39 | return obj; 40 | } 41 | 42 | public override object LoadObject() 43 | { 44 | object obj = new Version(Reader!.ReadString()); 45 | return obj; 46 | } 47 | 48 | public override void Clone(object source, object dest) 49 | { 50 | //dest = ((Version)source).Clone(); 51 | } 52 | } 53 | --------------------------------------------------------------------------------