From 5e6fb755ff1cb352376f7960647b54f0d823a359 Mon Sep 17 00:00:00 2001 From: Dongmin Jang Date: Wed, 5 Jun 2024 07:03:40 +0900 Subject: [PATCH] Move codes from dogzz9445/DDT --- Corathing.sln | 71 +- docs/roadmap.md | 48 + src/Apps/Corathing.Organizer/App.xaml | 3 +- src/Apps/Corathing.Organizer/App.xaml.cs | 127 +- .../Behaviors/DateTimeNowBehavior.cs | 33 + .../EntityConfigurationContext.cs | 143 ++ .../Controls/BaseWindow.xaml | 37 + .../Controls/BaseWindow.xaml.cs | 17 + .../Controls/TitlebarControl.xaml | 158 ++ .../Controls/TitlebarControl.xaml.cs | 205 +++ .../Corathing.Organizer.csproj | 19 + .../Extensions/WindowExtensions.cs | 118 ++ .../Services/AppStateService.cs | 12 + .../Services/ApplicationService.cs | 19 + .../Services/AuthService.cs | 93 + .../Utils/InterprocessCommunicationFactory.cs | 22 + .../Utils/SecondInstanceSerivce.cs | 100 ++ .../ViewModels/LoginViewModel.cs | 38 + .../ViewModels/MenuItemViewModel.cs | 23 + .../Views/DashboardView.xaml | 473 ++++++ .../Views/DashboardView.xaml.cs | 459 +++++ .../Corathing.Organizer/Views/LoginView.xaml | 14 + .../Views/LoginView.xaml.cs | 28 + .../Attributes/WidgetAttribute.cs | 40 + .../Bases/Interfaces/IEntity.cs | 13 + .../Bases/Interfaces/IProjectState.cs | 75 + .../Bases/Interfaces/IWidgetCoreState.cs | 11 + .../Bases/Interfaces/IWidgetGenerator.cs | 27 + .../Bases/Interfaces/IWidgetLayout.cs | 27 + .../Bases/Interfaces/IWidgetState.cs | 14 + .../Bases/Interfaces/IWorkflowState.cs | 27 + .../Corathing.Contracts/Bases/ProjectState.cs | 59 + .../Bases/WidgetContext.cs | 49 + .../Bases/WidgetCoreState.cs | 14 + .../Corathing.Contracts/Bases/WidgetEnv.cs | 49 + .../Bases/WidgetGenerator.cs | 80 + .../Corathing.Contracts/Bases/WidgetLayout.cs | 330 ++++ .../Corathing.Contracts/Bases/WidgetState.cs | 25 + .../Bases/WorkflowState.cs | 57 + .../Corathing.Contracts.csproj | 13 + .../Factories/WidgetLayoutFactory.cs | 27 + .../Helpers/CryptoHelper.cs | 118 ++ .../Corathing.Contracts/Helpers/NameHelper.cs | 28 + .../Messages/EntityUpdateMessage.cs | 15 + .../Services/IAppStateService.cs | 17 + .../Services/IApplicationService.cs | 12 + .../Services/IAuthService.cs | 45 + .../Services/IDialogService.cs | 11 + .../Services/IPackageService.cs | 16 + .../Services/IResourceDictionaryService.cs | 11 + .../Services/ISecretService.cs | 11 + .../Services/IStorageService.cs | 38 + .../Services/IThemeService.cs | 11 + .../Structures/EntityCollection.cs | 195 +++ .../Bindings/Localizer.cs | 152 ++ .../Bindings/PropertyChangedNotifier.cs | 109 ++ .../Controls/ControlHitType.cs | 6 + .../Controls/DashboardHost.xaml | 21 + .../Controls/DashboardHost.xaml.cs | 1508 +++++++++++++++++ .../Controls/DragAdorner.cs | 178 ++ .../Controls/WidgetHost.xaml | 193 +++ .../Controls/WidgetHost.xaml.cs | 201 +++ .../Converters/BoolToVisibilityConverter.cs | 57 + .../DashboardSelectorIsCheckedConverter.cs | 52 + .../DelayedMultiBindingExtension.cs | 239 +++ .../InvertBoolToVisibilityConverter.cs | 57 + .../InvertNullToVisibilityConverter.cs | 45 + .../Converters/NullToVisibilityConverter.cs | 28 + .../Corathing.Dashboards.WPF.csproj | 22 + .../DependencyInjectionExtensions.cs | 40 + .../Extensions/DependencyObjectExtensions.cs | 50 + .../Styles/CustomStyles.xaml | 264 +++ .../Corathing.Dashboards.WPF/Themes/Dark.xaml | 49 + .../Themes/Generic.xaml | 156 ++ .../Themes/Light.xaml | 50 + .../Widgets/EmptyWidget.xaml | 12 + .../Widgets/EmptyWidget.xaml.cs | 52 + .../Corathing.Dashboards.csproj | 13 + .../Services/PackageService.cs | 82 + 79 files changed, 7355 insertions(+), 6 deletions(-) create mode 100644 docs/roadmap.md create mode 100644 src/Apps/Corathing.Organizer/Behaviors/DateTimeNowBehavior.cs create mode 100644 src/Apps/Corathing.Organizer/Configurations/EntityConfigurationContext.cs create mode 100644 src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml create mode 100644 src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml.cs create mode 100644 src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml create mode 100644 src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml.cs create mode 100644 src/Apps/Corathing.Organizer/Extensions/WindowExtensions.cs create mode 100644 src/Apps/Corathing.Organizer/Services/AppStateService.cs create mode 100644 src/Apps/Corathing.Organizer/Services/ApplicationService.cs create mode 100644 src/Apps/Corathing.Organizer/Services/AuthService.cs create mode 100644 src/Apps/Corathing.Organizer/Utils/InterprocessCommunicationFactory.cs create mode 100644 src/Apps/Corathing.Organizer/Utils/SecondInstanceSerivce.cs create mode 100644 src/Apps/Corathing.Organizer/ViewModels/LoginViewModel.cs create mode 100644 src/Apps/Corathing.Organizer/ViewModels/MenuItemViewModel.cs create mode 100644 src/Apps/Corathing.Organizer/Views/DashboardView.xaml create mode 100644 src/Apps/Corathing.Organizer/Views/DashboardView.xaml.cs create mode 100644 src/Apps/Corathing.Organizer/Views/LoginView.xaml create mode 100644 src/Apps/Corathing.Organizer/Views/LoginView.xaml.cs create mode 100644 src/Shared/Corathing.Contracts/Attributes/WidgetAttribute.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IEntity.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IProjectState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IWidgetCoreState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IWidgetGenerator.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IWidgetLayout.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IWidgetState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/Interfaces/IWorkflowState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/ProjectState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WidgetContext.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WidgetCoreState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WidgetEnv.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WidgetGenerator.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WidgetLayout.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WidgetState.cs create mode 100644 src/Shared/Corathing.Contracts/Bases/WorkflowState.cs create mode 100644 src/Shared/Corathing.Contracts/Corathing.Contracts.csproj create mode 100644 src/Shared/Corathing.Contracts/Factories/WidgetLayoutFactory.cs create mode 100644 src/Shared/Corathing.Contracts/Helpers/CryptoHelper.cs create mode 100644 src/Shared/Corathing.Contracts/Helpers/NameHelper.cs create mode 100644 src/Shared/Corathing.Contracts/Messages/EntityUpdateMessage.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IAppStateService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IApplicationService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IAuthService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IDialogService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IPackageService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IResourceDictionaryService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/ISecretService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IStorageService.cs create mode 100644 src/Shared/Corathing.Contracts/Services/IThemeService.cs create mode 100644 src/Shared/Corathing.Contracts/Structures/EntityCollection.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Bindings/Localizer.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Bindings/PropertyChangedNotifier.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Controls/ControlHitType.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Controls/DashboardHost.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Controls/DashboardHost.xaml.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Controls/DragAdorner.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Controls/WidgetHost.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Controls/WidgetHost.xaml.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Converters/BoolToVisibilityConverter.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Converters/DashboardSelectorIsCheckedConverter.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Converters/DelayedMultiBindingExtension.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Converters/InvertBoolToVisibilityConverter.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Converters/InvertNullToVisibilityConverter.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Converters/NullToVisibilityConverter.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Corathing.Dashboards.WPF.csproj create mode 100644 src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyInjectionExtensions.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyObjectExtensions.cs create mode 100644 src/Shared/Corathing.Dashboards.WPF/Styles/CustomStyles.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Themes/Dark.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Themes/Generic.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Themes/Light.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml create mode 100644 src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml.cs create mode 100644 src/Shared/Corathing.Dashboards/Corathing.Dashboards.csproj create mode 100644 src/Shared/Corathing.Dashboards/Services/PackageService.cs diff --git a/Corathing.sln b/Corathing.sln index 32d3a43..5ece022 100644 --- a/Corathing.sln +++ b/Corathing.sln @@ -15,7 +15,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Widgets", "Widgets", "{B32F EndProject Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "Corathing.Package", "src\Apps\Corathing.Package\Corathing.Package.wapproj", "{38B75A92-3E9F-4198-9C9A-BF30A31BE45B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Corathing.Organizer.UnitTests", "src\Apps\Corathing.Organizer.UnitTests\Corathing.Organizer.UnitTests.csproj", "{20E9EB36-05A9-4C3D-8F19-6EB85DD4C733}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Corathing.Organizer.UnitTests", "src\Apps\Corathing.Organizer.UnitTests\Corathing.Organizer.UnitTests.csproj", "{20E9EB36-05A9-4C3D-8F19-6EB85DD4C733}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Corathing.Contracts", "src\Shared\Corathing.Contracts\Corathing.Contracts.csproj", "{C952CC3C-2AEE-44B3-931C-59877770A846}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Corathing.Dashboards.WPF", "src\Shared\Corathing.Dashboards.WPF\Corathing.Dashboards.WPF.csproj", "{2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Corathing.Dashboards", "src\Shared\Corathing.Dashboards\Corathing.Dashboards.csproj", "{B07D1162-D70E-4CCB-8F13-2E21611B9214}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -101,6 +107,66 @@ Global {20E9EB36-05A9-4C3D-8F19-6EB85DD4C733}.Release|x64.Build.0 = Release|Any CPU {20E9EB36-05A9-4C3D-8F19-6EB85DD4C733}.Release|x86.ActiveCfg = Release|Any CPU {20E9EB36-05A9-4C3D-8F19-6EB85DD4C733}.Release|x86.Build.0 = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|ARM.Build.0 = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|ARM64.Build.0 = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|x64.ActiveCfg = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|x64.Build.0 = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|x86.ActiveCfg = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Debug|x86.Build.0 = Debug|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|Any CPU.Build.0 = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|ARM.ActiveCfg = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|ARM.Build.0 = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|ARM64.ActiveCfg = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|ARM64.Build.0 = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|x64.ActiveCfg = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|x64.Build.0 = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|x86.ActiveCfg = Release|Any CPU + {C952CC3C-2AEE-44B3-931C-59877770A846}.Release|x86.Build.0 = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|ARM.Build.0 = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|ARM64.Build.0 = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|x64.ActiveCfg = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|x64.Build.0 = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|x86.ActiveCfg = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Debug|x86.Build.0 = Debug|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|Any CPU.Build.0 = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|ARM.ActiveCfg = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|ARM.Build.0 = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|ARM64.ActiveCfg = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|ARM64.Build.0 = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|x64.ActiveCfg = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|x64.Build.0 = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|x86.ActiveCfg = Release|Any CPU + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD}.Release|x86.Build.0 = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|ARM.ActiveCfg = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|ARM.Build.0 = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|ARM64.Build.0 = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|x64.ActiveCfg = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|x64.Build.0 = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|x86.ActiveCfg = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Debug|x86.Build.0 = Debug|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|Any CPU.Build.0 = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|ARM.ActiveCfg = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|ARM.Build.0 = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|ARM64.ActiveCfg = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|ARM64.Build.0 = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|x64.ActiveCfg = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|x64.Build.0 = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|x86.ActiveCfg = Release|Any CPU + {B07D1162-D70E-4CCB-8F13-2E21611B9214}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -109,6 +175,9 @@ Global {793A7A5D-A4F3-4853-ADDA-4BB38117DB24} = {5BF1289A-1427-4306-B2E1-51E0770B6B75} {38B75A92-3E9F-4198-9C9A-BF30A31BE45B} = {5BF1289A-1427-4306-B2E1-51E0770B6B75} {20E9EB36-05A9-4C3D-8F19-6EB85DD4C733} = {5BF1289A-1427-4306-B2E1-51E0770B6B75} + {C952CC3C-2AEE-44B3-931C-59877770A846} = {89CC8AAB-466A-47B6-98B9-93EB379F55A7} + {2B5148C8-F3C9-4B81-B156-1B6CF1FF4CAD} = {89CC8AAB-466A-47B6-98B9-93EB379F55A7} + {B07D1162-D70E-4CCB-8F13-2E21611B9214} = {89CC8AAB-466A-47B6-98B9-93EB379F55A7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FC027A8C-0C58-494B-BE76-F403F0336A74} diff --git a/docs/roadmap.md b/docs/roadmap.md new file mode 100644 index 0000000..d44d072 --- /dev/null +++ b/docs/roadmap.md @@ -0,0 +1,48 @@ +# Road Map + +## 1.0.0 + +### 기능 +- [ ] Dashboard host 및 widget host 구현 및 버그 수정, 테스트 +- [ ] Localization 서비스 구현 +- [ ] 테마 서비스 구현 +- [ ] Nuget 패키지에서 위젯을 읽고 로드하는 서비스 구현 + - [ ] DLL 로드 +- [ ] 개인적인 사용이 아닌 배포 용으로 사용할 때 필요한 작업들을 정리 + - [ ] 시크릿 서비스를 이용한 어플리케이션 설정 암호화 + - [ ] 문서에 빌드 방법 및 배포 방법을 정리 + +### 위젯 +- [ ] Python 위젯 +- [ ] C# 위젯 +- [ ] Windows 위젯 +- [ ] WebView 위젯 +- [ ] vscode 위젯 + + +### CI/CI +- [ ] Create CI/CD workflows for WPF Applications built on .NET 8.x +- [ ] Code Quality + +### 배포/웹사이트 +- [ ] Create a website for the project +- [ ] Create a GitHub Pages site for the project +- [ ] Create a NuGet package for the project +- [ ] Create a Community Standup for the project + +## 2.0.0 + +### 기능 +- [ ] Navigation 서비스 구현 + +### 위젯 +- [ ] PLC 위젯 + +### CI/CI +- [ ] + +### 배포/웹사이트 +- [ ] + + +## 3.0.0 diff --git a/src/Apps/Corathing.Organizer/App.xaml b/src/Apps/Corathing.Organizer/App.xaml index d5093c9..f3da1f8 100644 --- a/src/Apps/Corathing.Organizer/App.xaml +++ b/src/Apps/Corathing.Organizer/App.xaml @@ -1,8 +1,7 @@  + xmlns:local="clr-namespace:Corathing.Organizer"> diff --git a/src/Apps/Corathing.Organizer/App.xaml.cs b/src/Apps/Corathing.Organizer/App.xaml.cs index 8cfc087..98837c1 100644 --- a/src/Apps/Corathing.Organizer/App.xaml.cs +++ b/src/Apps/Corathing.Organizer/App.xaml.cs @@ -1,14 +1,135 @@ using System.Configuration; using System.Data; +using System.Diagnostics; +using System.IO; +using System.IO.Packaging; using System.Windows; -namespace Corathing.Organizer +using Corathing.Contracts.Services; +using Corathing.Dashboards.Services; +using Corathing.Organizer.Services; +using Corathing.Organizer.Utils; + +using MahApps.Metro.Controls; + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +using Wpf.Ui.Appearance; + +using Application = System.Windows.Application; +using MessageBox = System.Windows.MessageBox; + +namespace Corathing.Organizer; + +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application { + + /// + /// Gets the current instance of the application + /// + public new static App Current => (App)Application.Current; + /// - /// Interaction logic for App.xaml + /// Gets the instance of the application /// - public partial class App : Application + public IServiceProvider? Services { get; private set; } + + protected override void OnStartup(StartupEventArgs e) { + base.OnStartup(e); + + Application.Current.DispatcherUnhandledException += (sender, args) => + { + MessageBox.Show(args.Exception.Message, "Unhandled exception occured"); + //Logger.LogError(args.Exception, "Unhandled exception occured"); + }; + + // 같은 이름의 다른 프로세스가 실행중인지 확인하고, 실행중이면 종료 + if (CheckIfProcessExists()) + { + MessageBox.Show( + "Another instance of the application is already running.", + "Error", + MessageBoxButton.OK, + MessageBoxImage.Error); + + Shutdown(); + } + + // Set the theme + var theme = System.Configuration.ConfigurationManager.AppSettings["Theme"]; + //ThemeHelper.Register("Light", @"pack://application:,,,/DDT.Core.WidgetSystems;component/Themes/Light.xaml"); + //ThemeHelper.Register("Dark", @"pack://application:,,,/DDT.Core.WidgetSystems;component/Themes/Dark.xaml"); + //ThemeHelper.ChangeTheme(Resources, "Dark"); + ApplicationThemeManager.Apply(ApplicationTheme.Dark); + + Services = ConfigureServices(e.Args); + + // Create a new MainWindow and set its DataContext to a new MainWindowViewModel which binds the view to the viewmodel + new MainWindow().Show(); } + private static IConfigurationRoot BuildConfiguration(string[] args) + { + // Create and build a configuration builder + var builder = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + //.AddAppSettingsJsonFileByEnvironmentVariables() + //.AddEnvironmentVariables() + //.AddEntityConfiguration() + .AddCommandLine(args); + + return builder.Build(); + } + + private static IServiceProvider ConfigureServices(string[] args) + { + var serviceCollection = new ServiceCollection(); + + // Build the configuration + var configuration = BuildConfiguration(args); + serviceCollection.AddSingleton(configuration); + + // Register services + serviceCollection.AddSingleton(); + //serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + //serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + //serviceCollection.AddSingleton(); + + // Register viewmodels + + + //Logger.Configure(configuration); + //serviceCollection.AddSingleton(); + //Localizer.Configure(configuration); + + return serviceCollection.BuildServiceProvider(); + } + + /// + /// Checks if there is already an instance of Openhardwaremonitor running and brings up its window + /// in case its minimized or as icon in taskbar + /// + private static bool CheckIfProcessExists() + { + bool processExists = false; + Process thisInstance = Process.GetCurrentProcess(); + if (Process.GetProcessesByName(thisInstance.ProcessName).Length > 1) + { + processExists = true; + using (var clientPipe = InterprocessCommunicationFactory.GetClientPipe()) + { + clientPipe.Connect(); + clientPipe.Write(new byte[] { (byte)SecondInstanceService.SecondInstanceRequest.MaximizeWindow }, 0, 1); + } + } + + return processExists; + } } diff --git a/src/Apps/Corathing.Organizer/Behaviors/DateTimeNowBehavior.cs b/src/Apps/Corathing.Organizer/Behaviors/DateTimeNowBehavior.cs new file mode 100644 index 0000000..774e865 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Behaviors/DateTimeNowBehavior.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// From: https://github.com/MahApps/MahApps.Metro/blob/develop/src/MahApps.Metro.Samples/MahApps.Metro.Demo/Behaviors/DateTimeNowBehavior.cs + +using System; +using System.Collections.Generic; +using System.Windows.Threading; +using MahApps.Metro.Controls; + +using Microsoft.Xaml.Behaviors; + +namespace Corathing.Organizer.Behaviors; + +public class DateTimeNowBehavior : Behavior +{ + private DispatcherTimer? _dispatcherTimer; + + protected override void OnAttached() + { + base.OnAttached(); + this._dispatcherTimer = new DispatcherTimer(TimeSpan.FromSeconds(1), + DispatcherPriority.DataBind, + (sender, args) => this.AssociatedObject.SelectedDateTime = DateTime.Now, + Dispatcher.CurrentDispatcher); + } + + protected override void OnDetaching() + { + base.OnDetaching(); + this._dispatcherTimer?.Stop(); + } +} diff --git a/src/Apps/Corathing.Organizer/Configurations/EntityConfigurationContext.cs b/src/Apps/Corathing.Organizer/Configurations/EntityConfigurationContext.cs new file mode 100644 index 0000000..ab6dc8b --- /dev/null +++ b/src/Apps/Corathing.Organizer/Configurations/EntityConfigurationContext.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +namespace Corathing.Organizer.Configurations; + +public enum DatabaseConnectionType +{ + Default, + Sqlite, + MySql +} + +public class EntityConfigurationOption +{ + public const string Section = "WidgetSystem:Database"; + + /// + /// 선택된 DB 연결 타입 + /// + public DatabaseConnectionType? DatabaseType { get; set; } + + /// + /// Sqlite 연결 정보를 담은 문자열 + /// + public string? SqliteConnectionString { get; set; } + + public string? SqliteDatabaseFile { get; set; } + + /// + /// Mysql 연결 정보를 담은 문자열 + /// + public string? MySqlConnectionString { get; set; } + + /// + /// Mysql 문자 인코딩 설정 + /// + public string? MySqlCharSet { get; set; } + + /// + /// DatabaseOptions 생성자 + /// 기본 값들을 할당 + /// + public EntityConfigurationOption() + { + SqliteDatabaseFile = "database.db"; + DatabaseType = DatabaseConnectionType.Sqlite; + SqliteConnectionString = $"Data Source={SqliteDatabaseFile};Version=3;"; + MySqlConnectionString = $"server=;port=;database=;uid=;password="; + MySqlCharSet = "utf8mb4"; + } + + /// + /// OptionsBuilder를 옵션에 따라 설정합니다. + /// + /// + /// + public DbContextOptionsBuilder ConfigureOptionsBuilder(DbContextOptionsBuilder optionsBuilder) + { + return DatabaseType switch + { + //DatabaseConnectionType.MySql + // => optionsBuilder.UseMySql(MySqlConnectionString, ServerVersion.AutoDetect(MySqlConnectionString), + // builder => builder.EnableRetryOnFailure(10)), + DatabaseConnectionType.Sqlite + => optionsBuilder.UseSqlite(SqliteConnectionString), + _ or DatabaseConnectionType.Default + => optionsBuilder.UseSqlite(SqliteConnectionString), + }; + } +} + +public record EntitySettings(string Id, string? Value); + +public class EntityConfigurationSource( + EntityConfigurationOption? options) : IConfigurationSource +{ + public IConfigurationProvider Build(IConfigurationBuilder builder) => + new EntityConfigurationProvider(options); +} +public class EntityConfigurationProvider(EntityConfigurationOption? options) + : ConfigurationProvider +{ + public override void Load() + { + using var dbContext = new EntityConfigurationContext(options); + + dbContext.Database.EnsureCreated(); + + Data = dbContext.Settings.Any() + ? dbContext.Settings.ToDictionary(c => c.Id, c => c.Value, StringComparer.OrdinalIgnoreCase) + : CreateAndSaveDefaultValues(dbContext); + } + + static IDictionary CreateAndSaveDefaultValues( + EntityConfigurationContext context) + { + var settings = new Dictionary( + StringComparer.OrdinalIgnoreCase) + { + ["WidgetOptions:IsInitialized"] = "false", + }; + + context.Settings.AddRange( + settings.Select(kvp => new EntitySettings(kvp.Key, kvp.Value)) + .ToArray()); + + context.SaveChanges(); + + return settings; + } +} + +public class EntityConfigurationContext(EntityConfigurationOption? options = null) : DbContext +{ + public DbSet Settings => Set(); + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + options.ConfigureOptionsBuilder(optionsBuilder); + } +} + +public static class EntityConfigurationBuilderExtensions +{ + public static IConfigurationBuilder AddEntityConfiguration( + this IConfigurationBuilder builder) + { + var tempConfig = builder.Build(); + var options = new EntityConfigurationOption(); + tempConfig.GetSection(EntityConfigurationOption.Section).Bind(options); + + if (options == null) + return builder; + + return builder.Add(new EntityConfigurationSource(options)); + } +} diff --git a/src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml b/src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml new file mode 100644 index 0000000..ef37dd6 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml @@ -0,0 +1,37 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml.cs b/src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml.cs new file mode 100644 index 0000000..05946b9 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Controls/BaseWindow.xaml.cs @@ -0,0 +1,17 @@ +using System; + +using MahApps.Metro.Controls; + +namespace Corathing.Organizer.Controls +{ + /// + /// Interaction logic for BaseWindow.xaml + /// + public partial class BaseWindow : MetroWindow + { + public BaseWindow() + { + InitializeComponent(); + } + } +} diff --git a/src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml b/src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml new file mode 100644 index 0000000..3a06377 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml.cs b/src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml.cs new file mode 100644 index 0000000..94b1d30 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Controls/TitlebarControl.xaml.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +using CommunityToolkit.Mvvm.Input; + +namespace Corathing.Organizer.Controls; + +/// +/// Interaction logic for Titlebar.xaml +/// +public partial class TitlebarControl : UserControl +{ + #region CloseWindowCommand + public RelayCommand? CloseWindowCommand + { + get { return (RelayCommand)GetValue(CloseWindowCommandProperty); } + set { SetValue(CloseWindowCommandProperty, value); } + } + public static readonly DependencyProperty CloseWindowCommandProperty = + DependencyProperty.Register("CloseWindowCommand", + typeof(RelayCommand), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(null)); + #endregion + + #region MaximizeWindowCommand + public RelayCommand? MaximizeWindowCommand + { + get { return (RelayCommand)GetValue(MaximizeWindowCommandProperty); } + set { SetValue(MaximizeWindowCommandProperty, value); } + } + public static readonly DependencyProperty MaximizeWindowCommandProperty = + DependencyProperty.Register( + "MaximizeWindowCommand", + typeof(RelayCommand), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(null)); + + public bool VisibleMaximizeButton + { + get { return (bool)GetValue(VisibleMaximizeButtonProperty); } + set { SetValue(VisibleMaximizeButtonProperty, value); } + } + public static readonly DependencyProperty VisibleMaximizeButtonProperty = + DependencyProperty.Register( + "VisibleMaximizeButton", + typeof(bool), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(true)); + #endregion + + #region MinimizeWindowCommand + public RelayCommand? MinimizeWindowCommand + { + get { return (RelayCommand)GetValue(MinimizeWindowCommandProperty); } + set { SetValue(MinimizeWindowCommandProperty, value); } + } + public static readonly DependencyProperty MinimizeWindowCommandProperty = + DependencyProperty.Register( + "MinimizeWindowCommandCommand", + typeof(RelayCommand), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(null)); + + public bool VisibleMinimizeButton + { + get { return (bool)GetValue(VisibleMinimizeButtonProperty); } + set { SetValue(VisibleMinimizeButtonProperty, value); } + } + public static readonly DependencyProperty VisibleMinimizeButtonProperty = + DependencyProperty.Register( + "VisibleMinimizeButton", + typeof(bool), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(true)); + #endregion + + #region OpenSettingsCommand + public RelayCommand? OpenSettingsCommand + { + get { return (RelayCommand?)GetValue(OpenSettingsCommandProperty); } + set { SetValue(OpenSettingsCommandProperty, value); } + } + public static readonly DependencyProperty OpenSettingsCommandProperty = + DependencyProperty.Register( + "OpenSettingsCommand", + typeof(RelayCommand), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(null)); + public bool VisibleSettingsButton + { + get { return (bool)GetValue(VisibleSettingsButtonProperty); } + set { SetValue(VisibleSettingsButtonProperty, value); } + } + public static readonly DependencyProperty VisibleSettingsButtonProperty = + DependencyProperty.Register( + "VisibleSettingsButton", + typeof(bool), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(true)); + #endregion + + #region Title + public string Title + { + get { return (string)GetValue(TitleProperty); } + set { SetValue(TitleProperty, value); } + } + public static readonly DependencyProperty TitleProperty = + DependencyProperty.Register("Title", typeof(string), typeof(TitlebarControl), new UIPropertyMetadata()); + #endregion + + #region AuthProfile + public bool VisibleAuthProfile + { + get { return (bool)GetValue(VisibleAuthProfileProperty); } + set { SetValue(VisibleAuthProfileProperty, value); } + } + public static readonly DependencyProperty VisibleAuthProfileProperty = + DependencyProperty.Register( + "VisibleAuthProfile", + typeof(bool), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(true)); + #endregion + + #region DateTime + public bool VisibleDateTime + { + get { return (bool)GetValue(VisibleDateTimeProperty); } + set { SetValue(VisibleDateTimeProperty, value); } + } + public static readonly DependencyProperty VisibleDateTimeProperty = + DependencyProperty.Register( + "VisibleDateTime", + typeof(bool), + typeof(TitlebarControl), + new FrameworkPropertyMetadata(false)); + + #endregion + + public TitlebarControl() + { + InitializeComponent(); + + CloseWindowCommand ??= new RelayCommand(CloseWindow); + MinimizeWindowCommand ??= new RelayCommand(MinimizeWindow); + MaximizeWindowCommand ??= new RelayCommand(MaximizedWindow); + } + + private void MinimizeWindow() + { + var window = Window.GetWindow(this); + if (window == null) + { + return; + } + window.WindowState = WindowState.Minimized; + } + + private void MaximizedWindow() + { + var window = Window.GetWindow(this); + if (window == null) + { + return; + } + window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; + } + + /// + /// 프로그램을 종료 + /// + private void CloseWindow() + { + var window = Window.GetWindow(this); + if (window == null) + { + return; + } + window.Close(); + } + + private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + var window = Window.GetWindow(this); + if (window == null) + { + return; + } + window.DragMove(); + } +} diff --git a/src/Apps/Corathing.Organizer/Corathing.Organizer.csproj b/src/Apps/Corathing.Organizer/Corathing.Organizer.csproj index 722ffd8..b09520c 100644 --- a/src/Apps/Corathing.Organizer/Corathing.Organizer.csproj +++ b/src/Apps/Corathing.Organizer/Corathing.Organizer.csproj @@ -6,10 +6,29 @@ enable enable true + true + + + + + + + + + + + + + + + + + + diff --git a/src/Apps/Corathing.Organizer/Extensions/WindowExtensions.cs b/src/Apps/Corathing.Organizer/Extensions/WindowExtensions.cs new file mode 100644 index 0000000..14c437a --- /dev/null +++ b/src/Apps/Corathing.Organizer/Extensions/WindowExtensions.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace Corathing.Organizer.Extensions; + +public static class WindowExtensions +{ + public static void MaximizeToFirstMonitor(this Window window) + { + var primaryScreen = Screen.AllScreens.Where(s => s.Primary).FirstOrDefault(); + + if (primaryScreen != null) + { + MaximizeWindow(window, primaryScreen); + } + } + + public static void MaximizeToSecondMonitor(this Window window) + { + var secondScreen = Screen.AllScreens.Where(s => !s.Primary).FirstOrDefault(); + + if (secondScreen != null) + { + MaximizeWindow(window, secondScreen); + } + } + + public static void MaximizeToMonitor(this Window window, int monitor) + { + var screen = Screen.AllScreens.Where(s => s.DeviceName == string.Format(@"\\.\DISPLAY{0}", monitor)).FirstOrDefault(); + + if (screen != null) + { + MaximizeWindow(window, screen); + } + } + + public static void MaximizeWindow(this Window window, Screen screen) + { + if (!window.IsLoaded) + window.WindowStartupLocation = WindowStartupLocation.Manual; + + var workingArea = screen.WorkingArea; + window.Left = workingArea.Left; + window.Top = workingArea.Top; + window.Width = workingArea.Width; + window.Height = workingArea.Height; + window.WindowStyle = WindowStyle.None; + window.ResizeMode = ResizeMode.NoResize; + + if (window.IsLoaded) + window.WindowState = WindowState.Maximized; + } + + public static void CenterToFirstMonitor(this Window window) + { + var primaryScreen = Screen.AllScreens.Where(s => s.Primary).FirstOrDefault(); + + if (primaryScreen != null) + { + CenterWindow(window, primaryScreen); + } + } + + public static void CenterToSecondMonitor(this Window window) + { + var secondScreen = Screen.AllScreens.Where(s => !s.Primary).FirstOrDefault(); + + if (secondScreen != null) + { + CenterWindow(window, secondScreen); + } + } + + public static void CenterToMonitor(this Window window, int monitor) + { + var screen = Screen.AllScreens.Where(s => s.DeviceName == string.Format(@"\\.\DISPLAY{0}", monitor)).FirstOrDefault(); + + if (screen != null) + { + CenterWindow(window, screen); + } + } + + public static void CenterWindow(this Window window, Screen screen) + { + if (!window.IsLoaded) + window.WindowStartupLocation = WindowStartupLocation.Manual; + + var workingArea = screen.WorkingArea; + window.Left = workingArea.Left + (workingArea.Width - window.Width) / 2; + window.Top = workingArea.Top + (workingArea.Height - window.Height) / 2; + + if (window.IsLoaded) + window.WindowState = WindowState.Normal; + } + + public static void MinimizeWindow(this Window window) + { + window.WindowState = WindowState.Minimized; + } + + public static void CenterWindowToParent(this Window window) + { + if (window.Owner == null) + return; + + if (!window.IsLoaded) + window.WindowStartupLocation = WindowStartupLocation.CenterOwner; + + if (window.IsLoaded) + window.WindowState = WindowState.Normal; + } +} diff --git a/src/Apps/Corathing.Organizer/Services/AppStateService.cs b/src/Apps/Corathing.Organizer/Services/AppStateService.cs new file mode 100644 index 0000000..ac0613f --- /dev/null +++ b/src/Apps/Corathing.Organizer/Services/AppStateService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Corathing.Organizer.Services +{ + internal class AppStateService + { + } +} diff --git a/src/Apps/Corathing.Organizer/Services/ApplicationService.cs b/src/Apps/Corathing.Organizer/Services/ApplicationService.cs new file mode 100644 index 0000000..c597cb9 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Services/ApplicationService.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Corathing.Contracts.Services; + +namespace Corathing.Organizer.Services +{ + public class ApplicationService : IApplicationService + { + public async Task DispatchAsync(Func> callback) + { + var result = await App.Current.Dispatcher.InvokeAsync(callback); + return result.Result; + } + } +} diff --git a/src/Apps/Corathing.Organizer/Services/AuthService.cs b/src/Apps/Corathing.Organizer/Services/AuthService.cs new file mode 100644 index 0000000..b0a9dfb --- /dev/null +++ b/src/Apps/Corathing.Organizer/Services/AuthService.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using Corathing.Contracts.Helpers; +using Corathing.Contracts.Services; +using Corathing.Organizer.Configurations; + +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; + +namespace Corathing.Organizer.Services; + +public class AuthContext(EntityConfigurationOption? options = null) : DbContext +{ + public DbSet Settings => Set(); + public static readonly new string RNG_AUTH = GenerateRandomString(); + + public static string GenerateRandomString() + { + byte[] nonceBytes = new byte[32]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(nonceBytes); + } + return Convert.ToBase64String(nonceBytes); + } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var a = options.SqliteConnectionString; + var conn = new SqliteConnection(@"Data Source=yourSQLite.db;"); + conn.Open(); + + var command = conn.CreateCommand(); + command.CommandText = "PRAGMA key = password;"; + command.ExecuteNonQuery(); + + optionsBuilder.UseSqlite(conn); + + options.ConfigureOptionsBuilder(optionsBuilder); + } +} + +public class AuthService : IAuthService +{ + private readonly ISecretService _secretService; + + public string SecretKey { get; set; } + public bool UseAuthService { get; set; } = true; + public bool IsAuthorized { get; set; } = false; + + public AuthService(ISecretService secretService) + { + } + + public async Task RequestAuthentication(Task request) + { + var authenticationInfo = await request; + + return true; + } + + private AuthenticationResponse Authentication(AuthenticationInfo? authenticationInfo) + { + var response = new AuthenticationResponse(); + if (authenticationInfo == null) + { + response.Code = AuthenticationCode.Error; + return response; + } + string encrytedPassword = CryptoHelper.SHA256Hash(password: authenticationInfo.password); + + var builder = new ConfigurationBuilder() + .AddJsonFile("data") + .AddEntityConfiguration(); + var tempConfig = builder.Build(); + var entityConfiguration = new EntityConfigurationOption(); + tempConfig.GetSection(EntityConfigurationOption.Section).Bind(entityConfiguration); + var dbContext = new EntityConfigurationContext(entityConfiguration); + dbContext.Database.EnsureCreated(); + + if (File.Exists(entityConfiguration.SqliteDatabaseFile)) + { + //var authenticationInfo = await requestAuthentication; + } + return response; + } +} diff --git a/src/Apps/Corathing.Organizer/Utils/InterprocessCommunicationFactory.cs b/src/Apps/Corathing.Organizer/Utils/InterprocessCommunicationFactory.cs new file mode 100644 index 0000000..ec94742 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Utils/InterprocessCommunicationFactory.cs @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v.2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +// https://github.com/hexagon-oss/openhardwaremonitor + +using System.Diagnostics; +using System.IO.Pipes; + +namespace Corathing.Organizer.Utils; + +public sealed class InterprocessCommunicationFactory +{ + public static NamedPipeServerStream GetServerPipe() + { + return new NamedPipeServerStream(Process.GetCurrentProcess().ProcessName, PipeDirection.InOut); + } + + public static NamedPipeClientStream GetClientPipe() + { + return new NamedPipeClientStream(".", Process.GetCurrentProcess().ProcessName, PipeDirection.InOut); + } +} diff --git a/src/Apps/Corathing.Organizer/Utils/SecondInstanceSerivce.cs b/src/Apps/Corathing.Organizer/Utils/SecondInstanceSerivce.cs new file mode 100644 index 0000000..8b5c900 --- /dev/null +++ b/src/Apps/Corathing.Organizer/Utils/SecondInstanceSerivce.cs @@ -0,0 +1,100 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v.2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +// https://github.com/hexagon-oss/openhardwaremonitor + +using System.IO.Pipes; +using System.IO; + +namespace Corathing.Organizer.Utils; + + +/// +/// This class handles communication between two instances form the already running instance +/// +public sealed class SecondInstanceService : IDisposable +{ + private CancellationTokenSource m_cancellationToken; + private Task m_secondInstanceTask; + private NamedPipeServerStream m_serverPipe; + + public event Action OnSecondInstanceRequest; + + public SecondInstanceService() + { + m_cancellationToken = null; + m_secondInstanceTask = null; + m_serverPipe = null; + } + + public void Run() + { + m_cancellationToken = new CancellationTokenSource(); + m_secondInstanceTask = new Task(HandleRequestAsync, m_cancellationToken.Token); + m_secondInstanceTask.Start(); + } + + /// + /// Waits for incoming connections reads the content and distributes it via event + /// + private async void HandleRequestAsync() + { + m_serverPipe = InterprocessCommunicationFactory.GetServerPipe(); + while (m_cancellationToken.IsCancellationRequested == false) + { + await m_serverPipe.WaitForConnectionAsync(m_cancellationToken.Token); + while (m_serverPipe.IsConnected) + { + using (var stream = new MemoryStream()) + { + byte[] buffer = new byte[32]; + int bytesRead; + while ((bytesRead = m_serverPipe.Read(buffer, 0, buffer.Length)) > 0) + { + stream.Write(buffer, 0, bytesRead); + } + byte[] result = stream.ToArray(); + if (result.Length == 1) + { + SecondInstanceRequest request = (SecondInstanceRequest)result[0]; + new Task(() => OnSecondInstanceRequest?.Invoke(request)).Start(); + } + } + } + m_serverPipe.Disconnect(); + } + } + + /// + /// Cancels communication and disconnects from other instance if connected + /// + public void Cancel() + { + m_cancellationToken.Cancel(); + while (m_secondInstanceTask.IsCompleted == false) ; + + if (m_serverPipe.IsConnected) + { + m_serverPipe.Disconnect(); + } + m_serverPipe.Dispose(); + + m_serverPipe = null; + m_secondInstanceTask = null; + m_cancellationToken = null; + } + + public void Dispose() + { + if (m_cancellationToken != null) + { + Cancel(); + } + } + + public enum SecondInstanceRequest : byte + { + None = 0, + MaximizeWindow = 1 + } +} diff --git a/src/Apps/Corathing.Organizer/ViewModels/LoginViewModel.cs b/src/Apps/Corathing.Organizer/ViewModels/LoginViewModel.cs new file mode 100644 index 0000000..3dea732 --- /dev/null +++ b/src/Apps/Corathing.Organizer/ViewModels/LoginViewModel.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Messaging.Messages; +using Microsoft.Extensions.DependencyInjection; + +namespace Corathing.Organizer.ViewModels; + +public class LoginViewModel( + IServiceProvider _services + ) : ObservableRecipient +{ + protected override void OnActivated() + { + if (_services == null) + return; + var authService = _services.GetService(); + + if (authService == null) + // We use a method group here, but a lambda expression is also valid + Messenger.Register, string>(this, "Select", (r, m) => r.Receive(m)); + } + + /// + public void Receive(PropertyChangedMessage message) + { + //if (message.Sender.GetType() == typeof(NetworkComputerWidgetViewModel) + // // && message.PropertyName == nameof(NetworkComputerWidgetViewModel.SelectedPost) + // ) + //{ + // NetworkComputer = (NetworkComputer)message.NewValue; + //} + } +} diff --git a/src/Apps/Corathing.Organizer/ViewModels/MenuItemViewModel.cs b/src/Apps/Corathing.Organizer/ViewModels/MenuItemViewModel.cs new file mode 100644 index 0000000..2ce05eb --- /dev/null +++ b/src/Apps/Corathing.Organizer/ViewModels/MenuItemViewModel.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Input; + +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Corathing.Organizer.ViewModels; + +public partial class MenuItemViewModel : ObservableObject +{ + [ObservableProperty] + private string _header; + + [ObservableProperty] + private ICommand _command; + + [ObservableProperty] + private ObservableCollection _menuItems; +} diff --git a/src/Apps/Corathing.Organizer/Views/DashboardView.xaml b/src/Apps/Corathing.Organizer/Views/DashboardView.xaml new file mode 100644 index 0000000..e3a6dbf --- /dev/null +++ b/src/Apps/Corathing.Organizer/Views/DashboardView.xaml @@ -0,0 +1,473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Shared/Corathing.Dashboards.WPF/Controls/WidgetHost.xaml.cs b/src/Shared/Corathing.Dashboards.WPF/Controls/WidgetHost.xaml.cs new file mode 100644 index 0000000..60214af --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Controls/WidgetHost.xaml.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Corathing.Dashboards.WPF.Controls +{ + /// + /// Delegate for creating drag events providing a widgetHost as the parameter + /// + /// The widget host. + public delegate void DragEventHandler(WidgetHost widgetHost); + + /// + /// Delegate for creating drag events providing a widgetHost as the parameter + /// + /// The widget host. + public delegate void MouseEnterEventHandler(WidgetHost widgetHost); + + /// + /// Interaction logic for WidgetHost.xaml + /// + public partial class WidgetHost : ContentControl + { + #region Private Fields + + // For Resize + // The part of the rectangle under the mouse. + public ControlHitType MouseHitType = ControlHitType.None; + private Point? _mouseDownPoint; + public Point? MouseDownPoint { get => _mouseDownPoint; } + private bool? _isInOutlineArea; + private double _outlineGap = 20; + + #endregion Private Fields + + #region Public Properties + + /// + /// Gets the index of the host. + /// + /// The index of the host. + public Guid Id { get; set; } + + #endregion Public Properties + + #region Public Events + + /// + /// Occurs when [drag started]. + /// + public event DragEventHandler DragMoveStarted; + + /// + /// Occurs when [drag started]. + /// + public event DragEventHandler DragResizeStarted; + + /// + /// Occurs when [mouse over]. + /// + public event MouseEnterEventHandler MouseOver; + + #endregion Public Events + + public WidgetHost() + { + InitializeComponent(); + Loaded += WidgetHost_Loaded; + Unloaded += WidgetHost_Unloaded; + MouseEnter += (s, e) => MouseOver?.Invoke(this); + } + + #region Private Methods + + /// + /// Handles the MouseLeftButtonDown event of the Host control. + /// + /// The source of the event. + /// The instance containing the event data. + private void Host_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + _mouseDownPoint = e.GetPosition(this); + + // MyBorder의 위치와 크기를 가져옵니다. + Rect borderRect = new Rect(new Point(0, 0), this.RenderSize); + + // 외곽선 영역을 정의합니다. + Rect outerRect = new Rect(borderRect.X, borderRect.Y, + borderRect.Width, borderRect.Height); + + // 내부 영역을 정의합니다. + Rect innerRect = new Rect(borderRect.X + (_outlineGap / 2), borderRect.Y + (_outlineGap / 2), + borderRect.Width - _outlineGap, borderRect.Height - _outlineGap); + + MouseHitType = SetHitType(outerRect, Mouse.GetPosition(this)); + // 클릭한 위치가 외곽선 영역에 있는지 확인합니다. + _isInOutlineArea = outerRect.Contains( + _mouseDownPoint.Value.X, + _mouseDownPoint.Value.Y) + && !innerRect.Contains(_mouseDownPoint.Value.X, _mouseDownPoint.Value.Y); + + if (_isInOutlineArea == true) + DragResizeStarted?.Invoke(this); + } + + // Return a HitType value to indicate what is at the point. + private ControlHitType SetHitType(Rect rect, Point point) + { + double left = 0; + double top = 0; + double right = rect.Right; + double bottom = rect.Bottom; + if (point.X < left) return ControlHitType.None; + if (point.X > right) return ControlHitType.None; + if (point.Y < top) return ControlHitType.None; + if (point.Y > bottom) return ControlHitType.None; + + if (point.X - left < _outlineGap) + { + // Left edge. + if (point.Y - top < _outlineGap) return ControlHitType.TL; + if (bottom - point.Y < _outlineGap) return ControlHitType.BL; + return ControlHitType.L; + } + else if (right - point.X < _outlineGap) + { + // Right edge. + if (point.Y - top < _outlineGap) return ControlHitType.TR; + if (bottom - point.Y < _outlineGap) return ControlHitType.BR; + return ControlHitType.R; + } + if (point.Y - top < _outlineGap) return ControlHitType.T; + if (bottom - point.Y < _outlineGap) return ControlHitType.B; + return ControlHitType.Body; + } + + /// + /// Handles the MouseMove event of the Host control. Used to invoke a drag started if the proper + /// conditions have been met + /// + /// The source of the event. + /// The instance containing the event data. + private void Host_PreviewMouseMove(object sender, MouseEventArgs e) + { + var mouseMovePoint = e.GetPosition(this); + + // Check if we're "dragging" this control around. If not the return, otherwise invoke DragStarted event. + if (!(_mouseDownPoint.HasValue) || + e.LeftButton == MouseButtonState.Released || + Point.Subtract(_mouseDownPoint.Value, mouseMovePoint).Length < SystemParameters.MinimumHorizontalDragDistance && + Point.Subtract(_mouseDownPoint.Value, mouseMovePoint).Length < SystemParameters.MinimumVerticalDragDistance) + return; + + if (_isInOutlineArea == false) + DragMoveStarted?.Invoke(this); + } + + /// + /// Handles the Loaded event of the WidgetHost control. + /// + /// The source of the event. + /// The instance containing the event data. + private void WidgetHost_Loaded(object sender, RoutedEventArgs e) + { + Loaded -= WidgetHost_Loaded; + PreviewMouseLeftButtonDown += Host_MouseLeftButtonDown; + PreviewMouseMove += Host_PreviewMouseMove; + MouseLeave += WidgetHost_MouseLeave; + } + + /// + /// Handles the Unloaded event of the WidgetHost control. + /// + /// The source of the event. + /// The instance containing the event data. + private void WidgetHost_Unloaded(object sender, RoutedEventArgs e) + { + Unloaded -= WidgetHost_Unloaded; + PreviewMouseLeftButtonDown -= Host_MouseLeftButtonDown; + PreviewMouseMove -= Host_PreviewMouseMove; + MouseLeave -= WidgetHost_MouseLeave; + } + + private void WidgetHost_MouseLeave(object sender, MouseEventArgs e) + { + } + + #endregion Private Methods + } +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Converters/BoolToVisibilityConverter.cs b/src/Shared/Corathing.Dashboards.WPF/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 0000000..9a51189 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows; + +namespace Corathing.Dashboards.WPF.Converters; + +/// +/// +/// Converter for if bool value provided is true then set Visibility.Collapsed else Visibility.Visible +/// +/// +[ValueConversion(typeof(bool), typeof(Visibility))] +public class BoolToVisibilityConverter : IValueConverter +{ + #region Public Methods + + /// + /// + /// Converts a value. + /// + /// The value produced by the binding source. + /// The type of the binding target property. + /// The converter parameter to use. + /// The culture to use in the converter. + /// + /// A converted value. If the method returns , the valid null value is used. + /// + /// InvertBoolToVisibilityConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + return Visibility.Visible; + + if (!(value is bool boolValue)) + return null; + //throw new Exception($"{value} {nameof(InvertBoolToVisibilityConverter)} expects a boolean to passed in through its value parameter"); + + return boolValue ? Visibility.Visible : Visibility.Collapsed; + } + + /// + /// Converts a value. + /// + /// The value that is produced by the binding target. + /// The type to convert to. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value. If the method returns , the valid null value is used. + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion Public Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Converters/DashboardSelectorIsCheckedConverter.cs b/src/Shared/Corathing.Dashboards.WPF/Converters/DashboardSelectorIsCheckedConverter.cs new file mode 100644 index 0000000..0c76202 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Converters/DashboardSelectorIsCheckedConverter.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows; + +namespace Corathing.Dashboards.WPF.Converters; + +public class DashboardSelectorIsCheckedConverter : IMultiValueConverter +{ + #region Public Methods + + /// + /// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target. + /// + /// The array of values that the source bindings in the produces. The value indicates that the source binding has no value to provide for conversion. + /// The type of the binding target property. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value.If the method returns , the valid value is used.A return value of . indicates that the converter did not produce a value, and that the binding will use the if it is available, or else will use the default value.A return value of . indicates that the binding does not transfer the value or use the or the default value. + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values.Any(value => !(value is bool)) || values.Length < 1 || (bool)values[0] == false) + return false; + + for (var i = 1; i < values.Length; i++) + { + if ((bool)values[i]) + return false; + } + + return true; + } + + /// + /// Converts a binding target value to the source binding values. + /// + /// The value that the binding target produces. + /// The array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return. + /// The converter parameter to use. + /// The culture to use in the converter. + /// An array of values that have been converted from the target value back to the source values. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + return new[] { DependencyProperty.UnsetValue }; + } + + #endregion Public Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Converters/DelayedMultiBindingExtension.cs b/src/Shared/Corathing.Dashboards.WPF/Converters/DelayedMultiBindingExtension.cs new file mode 100644 index 0000000..fad8f26 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Converters/DelayedMultiBindingExtension.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.Windows.Data; +using System.Windows.Markup; +using System.Windows.Threading; +using System.Windows; + +namespace Corathing.Dashboards.WPF.Converters; + +/// +/// Provides a delayed multi-binding. This class cannot be inherited. +/// Implements the +/// Implements the +/// Implements the +/// +/// +/// +/// +[ContentProperty("Bindings")] +public sealed class DelayedMultiBindingExtension : MarkupExtension, IMultiValueConverter, INotifyPropertyChanged +{ + #region Private Fields + + private readonly DispatcherTimer _timer; + + private object _delayedValue; + + private object _startingValue; + + private bool _startingValueInitialSet; + + private object _unDelayedValue; + + #endregion Private Fields + + #region Public Events + + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion Public Events + + #region Public Properties + + /// + /// Gets the bindings. + /// + /// The bindings. + public Collection Bindings { get; } + + /// + /// Gets the change count. + /// + /// The change count. + public int ChangeCount { get; private set; } + + /// + /// Gets or sets the converter. + /// + /// The converter. + public IMultiValueConverter Converter { get; set; } + + /// + /// Gets or sets the converter culture. + /// + /// The converter culture. + public CultureInfo ConverterCulture { get; set; } + + /// + /// Gets or sets the converter parameter. + /// + /// The converter parameter. + public object ConverterParameter { get; set; } + + /// + /// Gets or sets the current value. + /// + /// The current value. + public object CurrentValue + { + get => _delayedValue; + set + { + _delayedValue = _unDelayedValue = value; + _timer.Stop(); + } + } + + /// + /// Gets or sets the delay. + /// + /// The delay. + public TimeSpan Delay + { + get => _timer.Interval; + set => _timer.Interval = value; + } + + /// + /// Gets or sets the mode. + /// + /// The mode. + public BindingMode Mode { get; set; } + + /// + /// Gets or sets the starting value. + /// + /// The starting value. + public object StartingValue + { + get => _startingValue; + set + { + if (_startingValueInitialSet) + return; + + _startingValue = value; + CurrentValue = value; + _startingValueInitialSet = true; + } + } + + /// + /// Gets or sets the update source trigger. + /// + /// The update source trigger. + public UpdateSourceTrigger UpdateSourceTrigger { get; set; } + + #endregion Public Properties + + #region Public Constructors + + /// + /// Initializes a new instance of the class. + /// + public DelayedMultiBindingExtension() + { + Bindings = new Collection(); + _timer = new DispatcherTimer(); + _timer.Tick += Timer_Tick; + _timer.Interval = TimeSpan.FromMilliseconds(10); + } + + #endregion Public Constructors + + #region Public Methods + + /// + /// Converts source values to a value for the binding target. The data binding engine calls this method when it propagates the values from source bindings to the binding target. + /// + /// The array of values that the source bindings in the produces. The value indicates that the source binding has no value to provide for conversion. + /// The type of the binding target property. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value.If the method returns , the valid value is used.A return value of . indicates that the converter did not produce a value, and that the binding will use the if it is available, or else will use the default value.A return value of . indicates that the binding does not transfer the value or use the or the default value. + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + var newValue = Converter.Convert(values.Take(values.Length - 1).ToArray(), + targetType, + ConverterParameter, + ConverterCulture ?? culture); + + if (Equals(newValue, _unDelayedValue)) + return _delayedValue; + + _unDelayedValue = newValue; + _timer.Stop(); + _timer.Start(); + + return _delayedValue; + } + + /// + /// Converts a binding target value to the source binding values. + /// + /// The value that the binding target produces. + /// The array of types to convert to. The array length indicates the number and types of values that are suggested for the method to return. + /// The converter parameter to use. + /// The culture to use in the converter. + /// An array of values that have been converted from the target value back to the source values. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + return Converter.ConvertBack(value, targetTypes, ConverterParameter, ConverterCulture ?? culture) + .Concat(new object[] { ChangeCount }).ToArray(); + } + + /// + /// When implemented in a derived class, returns an object that is provided as the value of the target property for this markup extension. + /// + /// A service provider helper that can provide services for the markup extension. + /// The object value to set on the property where the extension is applied. + public override object ProvideValue(IServiceProvider serviceProvider) + { + if (!(serviceProvider.GetService(typeof(IProvideValueTarget)) is IProvideValueTarget valueProvider)) + return null; + + var bindingTarget = valueProvider.TargetObject as DependencyObject; + var bindingProperty = valueProvider.TargetProperty as DependencyProperty; + + var multi = new MultiBinding + { + Converter = this, + Mode = Mode, + UpdateSourceTrigger = UpdateSourceTrigger + }; + + foreach (var binding in Bindings) + multi.Bindings.Add(binding); + + multi.Bindings.Add(new System.Windows.Data.Binding("ChangeCount") + { + Source = this, + Mode = BindingMode.OneWay + }); + + if (bindingTarget != null && bindingProperty != null) + BindingOperations.SetBinding(bindingTarget, bindingProperty, multi); + + return bindingProperty == null ? multi : bindingTarget?.GetValue(bindingProperty); + } + + #endregion Public Methods + + #region Private Methods + + private void Timer_Tick(object sender, EventArgs e) + { + _timer.Stop(); + _delayedValue = _unDelayedValue; + ChangeCount++; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChangeCount))); + } + + #endregion Private Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Converters/InvertBoolToVisibilityConverter.cs b/src/Shared/Corathing.Dashboards.WPF/Converters/InvertBoolToVisibilityConverter.cs new file mode 100644 index 0000000..553d0f3 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Converters/InvertBoolToVisibilityConverter.cs @@ -0,0 +1,57 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows; + +namespace Corathing.Dashboards.WPF.Converters; + +/// +/// +/// Converter for if bool value provided is true then set Visibility.Collapsed else Visibility.Visible +/// +/// +[ValueConversion(typeof(bool), typeof(Visibility))] +public class InvertBoolToVisibilityConverter : IValueConverter +{ + #region Public Methods + + /// + /// + /// Converts a value. + /// + /// The value produced by the binding source. + /// The type of the binding target property. + /// The converter parameter to use. + /// The culture to use in the converter. + /// + /// A converted value. If the method returns , the valid null value is used. + /// + /// InvertBoolToVisibilityConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value == null) + return Visibility.Visible; + + if (!(value is bool boolValue)) + return null; + //throw new Exception($"{value} {nameof(InvertBoolToVisibilityConverter)} expects a boolean to passed in through its value parameter"); + + return boolValue ? Visibility.Collapsed : Visibility.Visible; + } + + /// + /// Converts a value. + /// + /// The value that is produced by the binding target. + /// The type to convert to. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value. If the method returns , the valid null value is used. + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion Public Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Converters/InvertNullToVisibilityConverter.cs b/src/Shared/Corathing.Dashboards.WPF/Converters/InvertNullToVisibilityConverter.cs new file mode 100644 index 0000000..c83845e --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Converters/InvertNullToVisibilityConverter.cs @@ -0,0 +1,45 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows; + +namespace Corathing.Dashboards.WPF.Converters; + +/// +/// Converter that checks the value for null and if it is null return visible; else collapsed +/// Implements the +/// +/// +public class InvertNullToVisibilityConverter : IValueConverter +{ + #region Public Methods + + /// + /// Converts a value. + /// + /// The value produced by the binding source. + /// The type of the binding target property. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value. If the method returns , the valid null value is used. + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value == null ? Visibility.Visible : Visibility.Collapsed; + } + + /// + /// Converts a value. + /// + /// The value that is produced by the binding target. + /// The type to convert to. + /// The converter parameter to use. + /// The culture to use in the converter. + /// A converted value. If the method returns , the valid null value is used. + /// + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion Public Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Converters/NullToVisibilityConverter.cs b/src/Shared/Corathing.Dashboards.WPF/Converters/NullToVisibilityConverter.cs new file mode 100644 index 0000000..2fdc566 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Converters/NullToVisibilityConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using System.Windows; + +namespace Corathing.Dashboards.WPF.Converters; + +/// +/// Converter that checks the value for null and if it is null returns collapsed; else visible +/// Implements the +/// +/// +public class NullToVisibilityConverter : IValueConverter +{ + #region Public Methods + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value == null ? Visibility.Collapsed : Visibility.Visible; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + #endregion Public Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Corathing.Dashboards.WPF.csproj b/src/Shared/Corathing.Dashboards.WPF/Corathing.Dashboards.WPF.csproj new file mode 100644 index 0000000..a332249 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Corathing.Dashboards.WPF.csproj @@ -0,0 +1,22 @@ + + + + net8.0-windows + enable + enable + true + + + + + + + + + + + + + + + diff --git a/src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyInjectionExtensions.cs b/src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyInjectionExtensions.cs new file mode 100644 index 0000000..c4bbba3 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyInjectionExtensions.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.Extensions.DependencyInjection; + +namespace Corathing.Dashboards.WPF.Extensions; + +public static class DependencyInjectionExtensions +{ + public static IServiceCollection AddTransientFromNamespace( + this IServiceCollection services, + string namespaceName, + params Assembly[] assemblies + ) + { + foreach (Assembly assembly in assemblies) + { + IEnumerable types = assembly + .GetTypes() + .Where(x => + x.IsClass + && x.Namespace!.StartsWith(namespaceName, StringComparison.InvariantCultureIgnoreCase) + ); + + foreach (Type? type in types) + { + if (services.All(x => x.ServiceType != type)) + { + _ = services.AddTransient(type); + } + } + } + + return services; + } +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyObjectExtensions.cs b/src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyObjectExtensions.cs new file mode 100644 index 0000000..2a2cf2e --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Extensions/DependencyObjectExtensions.cs @@ -0,0 +1,50 @@ +using System; +using System.Windows; +using System.Windows.Media; + +namespace Corathing.Dashboards.WPF.Extensions; + +/// +/// Provides extension methods for DependencyObject's +/// +public static class DependencyObjectExtensions +{ + #region Public Methods + + /// + /// Recursively finds a child element of the parent with generic T type provided (FrameworkElement) and the provided childName. + /// + /// + /// The parent. + /// Name of the child. + /// T. + public static T FindChildElementByName(this DependencyObject parent, string childName) where T : FrameworkElement + { + // Since this is a recursive call check if the parent parameter is the FrameworkElement we want! + switch (parent) + { + case null: + return null; + case T parentType when parentType.Name == childName: + return parentType; + } + + var childrenCount = VisualTreeHelper.GetChildrenCount(parent); + + // Loop through each child of the parent and recursively check them through FindChildElementByName + // until the request child is found + for (var i = 0; i < childrenCount; i++) + { + var child = VisualTreeHelper.GetChild(parent, i); + + var foundChild = FindChildElementByName(child, childName); + + if (foundChild != null) + return foundChild; + } + + return null; + } + + #endregion Public Methods +} diff --git a/src/Shared/Corathing.Dashboards.WPF/Styles/CustomStyles.xaml b/src/Shared/Corathing.Dashboards.WPF/Styles/CustomStyles.xaml new file mode 100644 index 0000000..414a1d8 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Styles/CustomStyles.xaml @@ -0,0 +1,264 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/Corathing.Dashboards.WPF/Themes/Dark.xaml b/src/Shared/Corathing.Dashboards.WPF/Themes/Dark.xaml new file mode 100644 index 0000000..dce0d3b --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Themes/Dark.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/Corathing.Dashboards.WPF/Themes/Generic.xaml b/src/Shared/Corathing.Dashboards.WPF/Themes/Generic.xaml new file mode 100644 index 0000000..687972c --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Themes/Generic.xaml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + #484848 + #000000 + #212121 + #000000 + #000000 + #000000 + + + #666ad1 + #000000 + #303f9f + #000000 + #001970 + #000000 + + + #484848 + #FFFFFF + #212121 + #FFFFFF + #000000 + #FFFFFF + + + #666ad1 + #FFFFFF + #303f9f + #FFFFFF + #001970 + #FFFFFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + #cfe2ff + #e2e3e5 + #d1e7dd + #f8d7da + #fff3cd + #cff4fc + #fcfcfd + #ced4da + + + #031633 + #161719 + #051b11 + #2c0b0e + #332701 + #032830 + #343a40 + #1a1d20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/Corathing.Dashboards.WPF/Themes/Light.xaml b/src/Shared/Corathing.Dashboards.WPF/Themes/Light.xaml new file mode 100644 index 0000000..f5d207c --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Themes/Light.xaml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml b/src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml new file mode 100644 index 0000000..79df86d --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml.cs b/src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml.cs new file mode 100644 index 0000000..5e46496 --- /dev/null +++ b/src/Shared/Corathing.Dashboards.WPF/Widgets/EmptyWidget.xaml.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +using CommunityToolkit.Mvvm.ComponentModel; + +using Corathing.Contracts.Bases; + +namespace Corathing.Dashboards.WPF.Widgets +{ + public class EmptyWidgetOption + { + + } + + public partial class EmptyWidgetState : WidgetState + { + + } + + public partial class EmptyWidgetContext : WidgetContext + { + [ObservableProperty] + private EmptyWidgetState _emptyWidgetState; + + public EmptyWidgetContext(IServiceProvider services) : base(services) + { + } + } + + /// + /// Interaction logic for EmptyWidget.xaml + /// + public partial class EmptyWidget + { + public EmptyWidget() + { + InitializeComponent(); + } + } +} diff --git a/src/Shared/Corathing.Dashboards/Corathing.Dashboards.csproj b/src/Shared/Corathing.Dashboards/Corathing.Dashboards.csproj new file mode 100644 index 0000000..cc96bb0 --- /dev/null +++ b/src/Shared/Corathing.Dashboards/Corathing.Dashboards.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/src/Shared/Corathing.Dashboards/Services/PackageService.cs b/src/Shared/Corathing.Dashboards/Services/PackageService.cs new file mode 100644 index 0000000..c304335 --- /dev/null +++ b/src/Shared/Corathing.Dashboards/Services/PackageService.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +using Corathing.Contracts.Attributes; +using Corathing.Contracts.Bases; +using Corathing.Contracts.Services; + +namespace Corathing.Dashboards.Services; + +public class ProxyDomain : MarshalByRefObject +{ + public Assembly? GetAssembly(string assemblyPath) + { + try + { + return Assembly.Load(assemblyPath); + } + catch (Exception) + { + return null; + } + } +} +public class PackageService : IPackageService +{ + /// + /// Gets or sets the available widgets. + /// + /// The available widgets. + private readonly List _availableWidgets = new List(); + private readonly IServiceProvider _services; + + public PackageService(IServiceProvider services) + { + _services = services; + } + + + public void RegisterWidgets(List widgets) + { + _availableWidgets.AddRange(widgets); + } + public List GetAvailableWidgets() + { + return _availableWidgets; + } + + public void LoadWidgetsFromDLL(string pathDLL) + { + Assembly a = Assembly.LoadFrom(pathDLL); + var types = a.GetTypes().Where(t => typeof(WidgetContext).IsAssignableFrom(t)); + List widgets = new List(); + // TODO: + // 도메인 프록시 문제 해결 필요 + //var setup = new AppDomainSetup + //{ + // ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, + // PrivateBinPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + //}; + foreach (var type in types) + { + System.Reflection.MemberInfo info = type; + var attributes = info.GetCustomAttributes(true); + + for (int i = 0; i < attributes.Length; i++) + { + if (attributes[i] is WidgetAttribute) + { + var attribute = ((WidgetAttribute)attributes[i]); + attribute.RegisterServices(_services); + widgets.Add(attribute.WidgetGenerator); + } + } + } + + RegisterWidgets(widgets); + } +}