├── .gitattributes
├── .gitignore
├── License.txt
├── Readme.md
├── TheKingsTable.sln
├── TheKingsTable
├── App.axaml
├── App.axaml.cs
├── Assets
│ └── avalonia-logo.ico
├── CommonAvaloniaHandlers.cs
├── CommonInteractions.cs
├── Controls
│ ├── BitEditor.axaml
│ ├── BitEditor.axaml.cs
│ ├── EntityEditor.axaml
│ ├── EntityEditor.axaml.cs
│ ├── EntitySelector.axaml
│ ├── EntitySelector.axaml.cs
│ ├── FileSystemSelector.axaml
│ ├── FileSystemSelector.axaml.cs
│ ├── StageEditorToolMenu.cs
│ ├── StageEditorToolMenuStyles.axaml
│ ├── StageRenderer.cs
│ ├── StageTableLocationEditor.axaml
│ ├── StageTableLocationEditor.axaml.cs
│ ├── TextEditorWrapper.axaml
│ ├── TextEditorWrapper.axaml.cs
│ ├── TileMouseHelper.cs
│ ├── TileSelectionEditor.axaml
│ ├── TileSelectionEditor.axaml.cs
│ ├── TilesetViewer.cs
│ ├── WizardControl.cs
│ └── WizardStyles.axaml
├── Converters
│ ├── EntityListConverter.cs
│ ├── ShortConverter.cs
│ └── StageEntryListConverter.cs
├── Models
│ ├── AvaloniaClipboard.cs
│ └── EditorManager.cs
├── Program.cs
├── TheKingsTable.csproj
├── Utilities.cs
├── ViewLocator.cs
├── ViewModels
│ ├── Editors
│ │ ├── ProjectEditorViewModel.cs
│ │ ├── StageEditorViewModel.cs
│ │ ├── StageTableListViewModel.cs
│ │ └── TextScriptEditorViewModel.cs
│ ├── MainWindowViewModel.cs
│ ├── ViewModelBase.cs
│ ├── WizardViewModel.cs
│ └── WizardWindowViewModel.cs
├── Views
│ ├── Editors
│ │ ├── ProjectEditor.axaml
│ │ ├── StageEditor.axaml
│ │ ├── StageTableEditors.axaml
│ │ └── TextScriptEditor.axaml
│ ├── MainWindow.axaml
│ ├── MainWindow.axaml.cs
│ ├── WizardView.axaml
│ ├── WizardView.axaml.cs
│ ├── WizardWindow.axaml
│ └── WizardWindow.axaml.cs
├── nuget.config
└── tiletypes.png
└── appveyor.yml
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Brayconn/TheKingsTable/a6e20631ff60a28ff1fbd0d6cba702e147b81973/Readme.md
--------------------------------------------------------------------------------
/TheKingsTable.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.1.32414.318
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CaveStoryModdingFramework", "..\CaveStoryModdingFramework\CaveStoryModdingFramework\CaveStoryModdingFramework.csproj", "{00C92507-872A-47C4-A632-6946B0F0935E}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CaveStoryModdingFrameworkTests", "..\CaveStoryModdingFramework\CaveStoryModdingFrameworkTests\CaveStoryModdingFrameworkTests.csproj", "{B4CC6E9D-3860-434D-A2A0-814263286D6E}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PETools", "..\PETools\PETools\PETools.csproj", "{B3B5FCAC-3F15-4839-BB20-81DDF6047794}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TheKingsTable", "TheKingsTable\TheKingsTable.csproj", "{2A32BB3E-B1D1-460C-8733-D376EF42EFDC}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {8150F0E5-0B43-4B09-BFFC-4D0E1F2EFABC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {8150F0E5-0B43-4B09-BFFC-4D0E1F2EFABC}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {8150F0E5-0B43-4B09-BFFC-4D0E1F2EFABC}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {8150F0E5-0B43-4B09-BFFC-4D0E1F2EFABC}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {00C92507-872A-47C4-A632-6946B0F0935E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {00C92507-872A-47C4-A632-6946B0F0935E}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {00C92507-872A-47C4-A632-6946B0F0935E}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {00C92507-872A-47C4-A632-6946B0F0935E}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {4582AE75-D1E5-4228-BD5F-F9389DA86EA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {4582AE75-D1E5-4228-BD5F-F9389DA86EA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {4582AE75-D1E5-4228-BD5F-F9389DA86EA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {4582AE75-D1E5-4228-BD5F-F9389DA86EA4}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {B3B5FCAC-3F15-4839-BB20-81DDF6047794}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {B3B5FCAC-3F15-4839-BB20-81DDF6047794}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {B3B5FCAC-3F15-4839-BB20-81DDF6047794}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {B3B5FCAC-3F15-4839-BB20-81DDF6047794}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {CCF06D34-860E-4F13-BB3C-E46E8FA955A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {CCF06D34-860E-4F13-BB3C-E46E8FA955A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {CCF06D34-860E-4F13-BB3C-E46E8FA955A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {CCF06D34-860E-4F13-BB3C-E46E8FA955A2}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {2BCB2AFA-27DB-4B92-83F8-02B859F1E1F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {2BCB2AFA-27DB-4B92-83F8-02B859F1E1F4}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {2BCB2AFA-27DB-4B92-83F8-02B859F1E1F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {2BCB2AFA-27DB-4B92-83F8-02B859F1E1F4}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {01D4486A-D72A-421D-842F-AB35EF1CDFBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {01D4486A-D72A-421D-842F-AB35EF1CDFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {01D4486A-D72A-421D-842F-AB35EF1CDFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {01D4486A-D72A-421D-842F-AB35EF1CDFBB}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {2A32BB3E-B1D1-460C-8733-D376EF42EFDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {2A32BB3E-B1D1-460C-8733-D376EF42EFDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {2A32BB3E-B1D1-460C-8733-D376EF42EFDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {2A32BB3E-B1D1-460C-8733-D376EF42EFDC}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {B4CC6E9D-3860-434D-A2A0-814263286D6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {B4CC6E9D-3860-434D-A2A0-814263286D6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {B4CC6E9D-3860-434D-A2A0-814263286D6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {B4CC6E9D-3860-434D-A2A0-814263286D6E}.Release|Any CPU.Build.0 = Release|Any CPU
56 | EndGlobalSection
57 | GlobalSection(SolutionProperties) = preSolution
58 | HideSolutionNode = FALSE
59 | EndGlobalSection
60 | GlobalSection(ExtensibilityGlobals) = postSolution
61 | SolutionGuid = {890BEC3A-DD97-4A03-A27C-437F20F41290}
62 | EndGlobalSection
63 | EndGlobal
64 |
--------------------------------------------------------------------------------
/TheKingsTable/App.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/TheKingsTable/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Markup.Xaml;
4 | using TheKingsTable.ViewModels;
5 | using TheKingsTable.Views;
6 |
7 | namespace TheKingsTable
8 | {
9 | public class App : Application
10 | {
11 | public override void Initialize()
12 | {
13 | AvaloniaXamlLoader.Load(this);
14 | }
15 |
16 | public override void OnFrameworkInitializationCompleted()
17 | {
18 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
19 | {
20 | desktop.MainWindow = new MainWindow
21 | {
22 | DataContext = new MainWindowViewModel(),
23 | };
24 | }
25 |
26 | base.OnFrameworkInitializationCompleted();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/TheKingsTable/Assets/avalonia-logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Brayconn/TheKingsTable/a6e20631ff60a28ff1fbd0d6cba702e147b81973/TheKingsTable/Assets/avalonia-logo.ico
--------------------------------------------------------------------------------
/TheKingsTable/CommonAvaloniaHandlers.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls;
2 | using ReactiveUI;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using MessageBox.Avalonia;
9 | using MessageBox.Avalonia.DTO;
10 |
11 | namespace TheKingsTable
12 | {
13 | public static class CommonAvaloniaHandlers
14 | {
15 | static List PrepareFilters(FileSelection input)
16 | {
17 | var filters = new List(input.Filters.Count / 2);
18 | foreach (var filter in input.Filters)
19 | {
20 | filters.Add(new FileDialogFilter()
21 | {
22 | Name = filter.Item1,
23 | Extensions = new List() { filter.Item2 }
24 | });
25 | }
26 | return filters;
27 | }
28 | public static async Task ShowOpenFileBrowser(InteractionContext context, Window parent)
29 | {
30 | var o = new OpenFileDialog()
31 | {
32 | Title = context.Input.Title,
33 | AllowMultiple = false,
34 | Directory = context.Input.Start,
35 | };
36 | o.Filters = PrepareFilters(context.Input);
37 |
38 | var result = await o.ShowAsync(parent);
39 | //cancelling the dialog returns null on windows
40 | //but I think it returns empty array on mac...?
41 | context.SetOutput(result?.Length > 0 ? result[0] : null);
42 | }
43 |
44 | public static async Task ShowSaveFileBrowser(InteractionContext context, Window parent)
45 | {
46 | var s = new SaveFileDialog()
47 | {
48 | Title = context.Input.Title,
49 | Directory = context.Input.Start
50 | };
51 | s.Filters = PrepareFilters(context.Input);
52 |
53 | var result = await s.ShowAsync(parent);
54 | context.SetOutput(result);
55 | }
56 |
57 | public static async Task ShowFolderBrowser(InteractionContext context, Window parent)
58 | {
59 | var o = new OpenFolderDialog()
60 | {
61 | Title = context.Input.Title,
62 | Directory = context.Input.Start
63 | };
64 | var result = await o.ShowAsync(parent);
65 | context.SetOutput(result);
66 | }
67 |
68 | public static async Task ShowYesNoMessage(InteractionContext context, Window parent)
69 | {
70 | var m = MessageBoxManager.GetMessageBoxStandardWindow(new MessageBoxStandardParams
71 | {
72 | ButtonDefinitions = MessageBox.Avalonia.Enums.ButtonEnum.YesNo,
73 | ContentTitle = context.Input.Title,
74 | ContentMessage = context.Input.Question
75 | });
76 | var res = await m.ShowDialog(parent);
77 | context.SetOutput(res == MessageBox.Avalonia.Enums.ButtonResult.Yes);
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/TheKingsTable/CommonInteractions.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Reactive;
5 |
6 | namespace TheKingsTable
7 | {
8 | public class FileSelection : FolderSelection
9 | {
10 | public List> Filters { get; }
11 | public FileSelection(string title, List> filters, string start = "") : base(title, start)
12 | {
13 | Filters = filters;
14 | }
15 | }
16 | public class FolderSelection
17 | {
18 | public string Title { get; }
19 | public string Start { get; set; }
20 | public FolderSelection(string title, string start = "")
21 | {
22 | Title = title;
23 | Start = start;
24 | }
25 | }
26 | public class Words
27 | {
28 | public string Title { get; }
29 | public string Question { get; }
30 |
31 | public Words(string title, string question)
32 | {
33 | Title = title;
34 | Question = question;
35 | }
36 | }
37 | public static class CommonInteractions
38 | {
39 | public static readonly Interaction BrowseToOpenFile = new Interaction();
40 | public static readonly Interaction BrowseToSaveFile = new Interaction();
41 | public static readonly Interaction BrowseForFolder = new Interaction();
42 | public static readonly Interaction IsOk = new Interaction();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/BitEditor.axaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
24 |
25 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/BitEditor.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 |
6 | namespace TheKingsTable.Controls
7 | {
8 | [TemplatePart(Name = PART_BitList, Type = typeof(ListBox))]
9 | public class BitEditor : TemplatedControl
10 | {
11 | public const string PART_BitList = nameof(PART_BitList);
12 |
13 | #region Length Styled Property
14 | public static readonly StyledProperty LengthProperty =
15 | AvaloniaProperty.Register(nameof(Length), 0);
16 |
17 | public int Length
18 | {
19 | get => GetValue(LengthProperty);
20 | set => SetValue(LengthProperty, value);
21 | }
22 | #endregion
23 |
24 | #region Value Styled Property
25 | public static readonly StyledProperty ValueProperty =
26 | AvaloniaProperty.Register(nameof(Value), null);
27 |
28 | public long? Value
29 | {
30 | get => GetValue(ValueProperty);
31 | set => SetValue(ValueProperty, value);
32 | }
33 | #endregion
34 |
35 | public BitEditor()
36 | {
37 | //LengthProperty.Changed.Subscribe(x => UpdateBitAmount(x.NewValue.Value));
38 | //ValueProperty.Changed.Subscribe(x => UpdateBitValues(x.NewValue.Value));
39 | }
40 |
41 | ListBox BitList;
42 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
43 | {
44 | base.OnApplyTemplate(e);
45 | BitList = e.NameScope.Find(PART_BitList);
46 | }
47 |
48 | public void UpdateBitAmount(int amount)
49 | {
50 |
51 | }
52 | public void UpdateBitValues(long? value)
53 | {
54 |
55 | }
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/EntityEditor.axaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
45 |
46 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/EntityEditor.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using Avalonia.Data;
6 | using System;
7 |
8 | namespace TheKingsTable.Controls
9 | {
10 | [TemplatePart(Name = PART_XEditor, Type = typeof(TextBox))]
11 | [TemplatePart(Name = PART_YEditor, Type = typeof(TextBox))]
12 | [TemplatePart(Name = PART_FlagEditor, Type = typeof(TextBox))]
13 | [TemplatePart(Name = PART_EventEditor, Type = typeof(TextBox))]
14 | [TemplatePart(Name = PART_TypeEditor, Type = typeof(TextBox))]
15 | [TemplatePart(Name = PART_RawBitEditor, Type = typeof(TextBox))]
16 | [TemplatePart(Name = PART_ListBitEditor, Type = typeof(BitEditor))]
17 | public class EntityEditor : TemplatedControl
18 | {
19 | public const string PART_XEditor = nameof(PART_XEditor);
20 | public const string PART_YEditor = nameof(PART_YEditor);
21 | public const string PART_FlagEditor = nameof(PART_FlagEditor);
22 | public const string PART_EventEditor = nameof(PART_EventEditor);
23 | public const string PART_TypeEditor = nameof(PART_TypeEditor);
24 | public const string PART_RawBitEditor = nameof(PART_RawBitEditor);
25 | public const string PART_ListBitEditor = nameof(PART_ListBitEditor);
26 |
27 | TextBox XEditor, YEditor, FlagEditor, EventEditor, TypeEditor, RawBitEditor;
28 | BitEditor ListBitEditor;
29 |
30 | #region X Styled Property
31 | public static readonly StyledProperty XProperty =
32 | AvaloniaProperty.Register(nameof(X), null, defaultBindingMode:BindingMode.TwoWay);
33 |
34 | public short? X
35 | {
36 | get => GetValue(XProperty);
37 | set => SetValue(XProperty, value);
38 | }
39 | #endregion
40 |
41 | #region Y Styled Property
42 | public static readonly StyledProperty YProperty =
43 | AvaloniaProperty.Register(nameof(Y), null, defaultBindingMode: BindingMode.TwoWay);
44 |
45 | public short? Y
46 | {
47 | get => GetValue(YProperty);
48 | set => SetValue(YProperty, value);
49 | }
50 | #endregion
51 |
52 | #region Flag Styled Property
53 | public static readonly StyledProperty FlagProperty =
54 | AvaloniaProperty.Register(nameof(Flag), null, defaultBindingMode: BindingMode.TwoWay);
55 |
56 | public short? Flag
57 | {
58 | get => GetValue(FlagProperty);
59 | set => SetValue(FlagProperty, value);
60 | }
61 | #endregion
62 |
63 | #region Event Styled Property
64 | public static readonly StyledProperty EventProperty =
65 | AvaloniaProperty.Register(nameof(Event), null, defaultBindingMode: BindingMode.TwoWay);
66 |
67 | public short? Event
68 | {
69 | get => GetValue(EventProperty);
70 | set => SetValue(EventProperty, value);
71 | }
72 | #endregion
73 |
74 | #region Type Styled Property
75 | public static readonly StyledProperty TypeProperty =
76 | AvaloniaProperty.Register(nameof(Type), null, defaultBindingMode: BindingMode.TwoWay);
77 |
78 | public short? Type
79 | {
80 | get => GetValue(TypeProperty);
81 | set => SetValue(TypeProperty, value);
82 | }
83 | #endregion
84 |
85 | #region Bits Styled Property
86 | public static readonly StyledProperty BitsProperty =
87 | AvaloniaProperty.Register(nameof(Bits), null, defaultBindingMode: BindingMode.TwoWay);
88 |
89 | public short? Bits
90 | {
91 | get => GetValue(BitsProperty);
92 | set => SetValue(BitsProperty, value);
93 | }
94 | #endregion
95 |
96 | Control lastFocused;
97 | Control LastFocused
98 | {
99 | get => lastFocused;
100 | set
101 | {
102 | if(value != LastFocused)
103 | {
104 | if (lastFocused == XEditor)
105 | SetIfDifferent(XProperty, GetNumber(XEditor.Text));
106 | else if (LastFocused == YEditor)
107 | SetIfDifferent(YProperty, GetNumber(YEditor.Text));
108 | else if (LastFocused == FlagEditor)
109 | SetIfDifferent(FlagProperty, GetFlag(FlagEditor.Text));
110 | else if (LastFocused == EventEditor)
111 | SetIfDifferent(EventProperty, short.TryParse(EventEditor.Text, out var e) ? e : null);
112 | else if (LastFocused == TypeEditor)
113 | SetIfDifferent(TypeProperty, GetEntityType(TypeEditor.Text));
114 | else if(LastFocused == RawBitEditor)
115 | {
116 | if (short.TryParse(RawBitEditor.Text, out var b))
117 | ListBitEditor.Value = cachedBits = b;
118 | if (value != ListBitEditor)
119 | SetIfDifferent(BitsProperty, cachedBits);
120 | }
121 | else if(LastFocused == ListBitEditor)
122 | {
123 | RawBitEditor.Text = (cachedBits = (short)ListBitEditor.Value).ToString();
124 | if (value != RawBitEditor)
125 | SetIfDifferent(BitsProperty, cachedBits);
126 | }
127 | lastFocused = value;
128 | }
129 | }
130 | }
131 |
132 | void SetIfDifferent(StyledProperty prop, short? value)
133 | {
134 | if(value != null && GetValue(prop) != value)
135 | SetValue(prop, value);
136 | }
137 |
138 | short? GetNumber(string text)
139 | {
140 | return short.TryParse(text, out var num) ? num : null;
141 | }
142 | //TODO get from project
143 | short? GetFlag(string text)
144 | {
145 | return short.TryParse(text, out var flag) ? flag : null;
146 | }
147 | //TODO get from project
148 | private short? GetEntityType(string text)
149 | {
150 | return short.TryParse(text, out var type) ? Type : null;
151 | }
152 |
153 | short cachedBits = 0;
154 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
155 | {
156 | base.OnApplyTemplate(e);
157 | XEditor = e.NameScope.Find(PART_XEditor);
158 | YEditor = e.NameScope.Find(PART_YEditor);
159 | FlagEditor = e.NameScope.Find(PART_FlagEditor);
160 | EventEditor = e.NameScope.Find(PART_EventEditor);
161 | TypeEditor = e.NameScope.Find(PART_TypeEditor);
162 | RawBitEditor = e.NameScope.Find(PART_RawBitEditor);
163 | ListBitEditor = e.NameScope.Find(PART_ListBitEditor);
164 |
165 | XEditor.GotFocus += UpdateFocus;
166 | YEditor.GotFocus += UpdateFocus;
167 | FlagEditor.GotFocus += UpdateFocus;
168 | EventEditor.GotFocus += UpdateFocus;
169 | TypeEditor.GotFocus += UpdateFocus;
170 | RawBitEditor.GotFocus += UpdateFocus;
171 | ListBitEditor.GotFocus += UpdateFocus;
172 |
173 | XEditor.LostFocus += UpdateFocus;
174 | YEditor.LostFocus += UpdateFocus;
175 | FlagEditor.LostFocus += UpdateFocus;
176 | EventEditor.LostFocus += UpdateFocus;
177 | TypeEditor.LostFocus += UpdateFocus;
178 | RawBitEditor.LostFocus += UpdateFocus;
179 | ListBitEditor.LostFocus += UpdateFocus;
180 |
181 | XProperty.Changed.Subscribe(x => XEditor.Text = x.NewValue.Value.ToString());
182 | YProperty.Changed.Subscribe(x => YEditor.Text = x.NewValue.Value.ToString());
183 | FlagProperty.Changed.Subscribe(x => FlagEditor.Text = x.NewValue.Value.ToString()); //TODO convert to proper value
184 | EventProperty.Changed.Subscribe(x => EventEditor.Text = x.NewValue.ToString());
185 | TypeProperty.Changed.Subscribe(x => TypeEditor.Text = x.NewValue.Value.ToString());
186 | BitsProperty.Changed.Subscribe(x =>
187 | {
188 | RawBitEditor.Text = x.NewValue.Value.ToString();
189 | ListBitEditor.Value = x.NewValue.Value;
190 | });
191 | }
192 |
193 | private void UpdateFocus(object? sender, EventArgs e)
194 | {
195 | if (sender is Control c)
196 | LastFocused = c;
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/EntitySelector.axaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
33 |
34 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/EntitySelector.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using Avalonia.Interactivity;
6 | using CaveStoryModdingFramework.Entities;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 |
10 | namespace TheKingsTable.Controls
11 | {
12 | [TemplatePart(Name = PART_EntityList, Type = typeof(ListBox))]
13 | public class EntitySelector : TemplatedControl
14 | {
15 | public const string PART_EntityList = nameof(PART_EntityList);
16 |
17 | #region SelectedEntity Styled Property
18 | public static readonly StyledProperty SelectedEntityProperty =
19 | AvaloniaProperty.Register(nameof(SelectedEntity), 0, defaultBindingMode:Avalonia.Data.BindingMode.TwoWay);
20 |
21 | public short SelectedEntity
22 | {
23 | get => GetValue(SelectedEntityProperty);
24 | set => SetValue(SelectedEntityProperty, value);
25 | }
26 | #endregion
27 |
28 | #region Entities Styled Property
29 | public static readonly StyledProperty?> EntitiesProperty =
30 | AvaloniaProperty.Register?>(nameof(Entities), null);
31 |
32 | public Dictionary? Entities
33 | {
34 | get => GetValue(EntitiesProperty);
35 | set => SetValue(EntitiesProperty, value);
36 | }
37 | #endregion
38 |
39 | ListBox EntityList;
40 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
41 | {
42 | base.OnApplyTemplate(e);
43 | EntityList = e.NameScope.Find(PART_EntityList);
44 | EntityList.SelectionChanged += SelectionChanged;
45 | }
46 |
47 | void SelectionChanged(object? sender, SelectionChangedEventArgs e)
48 | {
49 | if(Entities != null && e.AddedItems.Count > 0 && e.AddedItems[0] is KeyValuePair ent)
50 | {
51 | SelectedEntity = (short)ent.Key;
52 | //SelectedEntity = (short)Entities.First(x => x.Value == ent).Key;
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/FileSystemSelector.axaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/FileSystemSelector.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Interactivity;
4 | using Avalonia.Markup.Xaml;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using System.Reactive.Linq;
9 |
10 | namespace TheKingsTable.Controls
11 | {
12 | public partial class FileSystemSelector : UserControl
13 | {
14 | #region Text Styled Property
15 | public static readonly StyledProperty TextProperty =
16 | //TextBox.TextProperty.AddOwner();
17 | AvaloniaProperty.Register(nameof(Text));
18 | public string Text { get => GetValue(TextProperty); set => SetValue(TextProperty, value); }
19 | #endregion
20 |
21 | #region StartDirectory Styled Property
22 | public static readonly StyledProperty StartDirectoryProperty =
23 | AvaloniaProperty.Register(nameof(StartDirectory));
24 | public string StartDirectory { get => GetValue(StartDirectoryProperty); set => SetValue(StartDirectoryProperty, value); }
25 | #endregion
26 |
27 | #region Watermark Styled Property
28 | public static readonly StyledProperty WatermarkProperty =
29 | TextBox.WatermarkProperty.AddOwner();
30 | //AvaloniaProperty.Register(nameof(Watermark));
31 | public string Watermark { get => GetValue(WatermarkProperty); set => SetValue(WatermarkProperty, value); }
32 | #endregion
33 |
34 | #region Description Styled Property
35 | public static readonly StyledProperty DescriptionProperty =
36 | AvaloniaProperty.Register(nameof(Description));
37 | public string Description { get => GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); }
38 | #endregion
39 |
40 | #region WindowTitle Styled Property
41 | public static readonly StyledProperty WindowTitleProperty =
42 | AvaloniaProperty.Register(nameof(WindowTitle));
43 | public string WindowTitle { get => GetValue(WindowTitleProperty); set => SetValue(WindowTitleProperty, value); }
44 | #endregion
45 |
46 | #region Filters Styled Properties
47 | public static readonly StyledProperty FiltersProperty =
48 | AvaloniaProperty.Register(nameof(Filters));
49 | public string Filters { get => GetValue(FiltersProperty); set => SetValue(FiltersProperty, value); }
50 | #endregion
51 |
52 | #region IsSave Styled Property
53 | public static readonly StyledProperty IsSaveProperty =
54 | AvaloniaProperty.Register(nameof(IsSave), false);
55 |
56 | public bool IsSave
57 | {
58 | get => GetValue(IsSaveProperty);
59 | set => SetValue(IsSaveProperty, value);
60 | }
61 | #endregion
62 |
63 | public FileSystemSelector()
64 | {
65 | InitializeComponent();
66 | }
67 |
68 | private void InitializeComponent()
69 | {
70 | AvaloniaXamlLoader.Load(this);
71 | }
72 |
73 | public async void OnBrowse(object sender, RoutedEventArgs e)
74 | {
75 | string? result = null;
76 |
77 | string start;
78 | if (!string.IsNullOrWhiteSpace(Text))
79 | start = Path.GetDirectoryName(Text) ?? "";
80 | else
81 | start = StartDirectory;
82 |
83 | //filters means we're finding files
84 | if (!string.IsNullOrEmpty(Filters))
85 | {
86 | var splitFilters = Filters.Split("|");
87 | var formattedFilters = new List>(splitFilters.Length / 2);
88 | for (int i = 0; i < splitFilters.Length; i += 2)
89 | formattedFilters.Add(new Tuple(splitFilters[i], splitFilters[i + 1]));
90 |
91 | var interaction = IsSave ? CommonInteractions.BrowseToSaveFile : CommonInteractions.BrowseToOpenFile;
92 |
93 | result = await interaction.Handle(new FileSelection(WindowTitle, formattedFilters, start));
94 | }
95 | //none means we're finding directories
96 | else
97 | {
98 | result = await CommonInteractions.BrowseForFolder
99 | .Handle(new FolderSelection(WindowTitle, start));
100 | }
101 | if (result != null)
102 | {
103 | Text = result;
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/StageEditorToolMenu.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using Avalonia.Data;
6 | using Avalonia.Styling;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 | using TheKingsTable.ViewModels.Editors;
13 |
14 | namespace TheKingsTable.Controls
15 | {
16 | [TemplatePart(Name = PART_Tabs, Type = typeof(TabControl))]
17 | public class StageEditorToolMenu : TemplatedControl, IStyleable
18 | {
19 | public const string PART_Tabs = nameof(PART_Tabs);
20 |
21 | Type IStyleable.StyleKey => typeof(StageEditorToolMenu);
22 |
23 | #region StageEditor Styled Property
24 | public static readonly StyledProperty StageEditorProperty =
25 | AvaloniaProperty.Register(nameof(StageEditor), null);
26 |
27 | public StageEditorViewModel? StageEditor
28 | {
29 | get => GetValue(StageEditorProperty);
30 | set => SetValue(StageEditorProperty, value);
31 | }
32 | #endregion
33 |
34 | #region EditorMode Styled Property
35 | public static readonly StyledProperty EditorModeProperty =
36 | AvaloniaProperty.Register(nameof(EditorMode), StageEditorViewModel.Editors.Tile, defaultBindingMode:BindingMode.TwoWay);
37 |
38 | public StageEditorViewModel.Editors EditorMode
39 | {
40 | get => GetValue(EditorModeProperty);
41 | set => SetValue(EditorModeProperty, value);
42 | }
43 | #endregion
44 |
45 | #region TabType Attached Property
46 | public static readonly AttachedProperty TabTypeProperty
47 | = AvaloniaProperty.RegisterAttached(
48 | "TabType", null);
49 |
50 | public static void SetTabType(AvaloniaObject element, StageEditorViewModel.Editors? value)
51 | {
52 | element.SetValue(TabTypeProperty, value);
53 | }
54 |
55 | public static StageEditorViewModel.Editors? GetTabType(AvaloniaObject element)
56 | {
57 | return element.GetValue(TabTypeProperty);
58 | }
59 | #endregion
60 |
61 | TabControl Tabs;
62 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
63 | {
64 | base.OnApplyTemplate(e);
65 | Tabs = e.NameScope.Find(PART_Tabs);
66 | Tabs.SelectionChanged += Tabs_SelectionChanged;
67 | }
68 |
69 | private void Tabs_SelectionChanged(object? sender, SelectionChangedEventArgs e)
70 | {
71 | if(e.AddedItems.Count > 0 && e.AddedItems[0] is AvaloniaObject a)
72 | {
73 | var t = GetTabType(a);
74 | if (t != null)
75 | EditorMode = t.Value;
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/StageEditorToolMenuStyles.axaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
49 |
50 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/StageTableLocationEditor.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/StageTableLocationEditor.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace TheKingsTable.Controls
6 | {
7 | public partial class StageTableLocationEditor : UserControl
8 | {
9 | public StageTableLocationEditor()
10 | {
11 | InitializeComponent();
12 | }
13 |
14 | private void InitializeComponent()
15 | {
16 | AvaloniaXamlLoader.Load(this);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/TextEditorWrapper.axaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/TextEditorWrapper.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using AvaloniaEdit;
6 |
7 | namespace TheKingsTable.Controls
8 | {
9 | [TemplatePart(Name = PART_TextEditor, Type = typeof(TextEditor))]
10 | public class TextEditorWrapper : TemplatedControl
11 | {
12 | public const string PART_TextEditor = nameof(PART_TextEditor);
13 |
14 | #region Text Direct Property
15 | public static readonly DirectProperty TextProperty =
16 | AvaloniaProperty.RegisterDirect(nameof(Text),
17 | o => o.Text, (o,e) => o.Text = e, defaultBindingMode:Avalonia.Data.BindingMode.TwoWay);
18 |
19 | //This only exists because binding takes place before the TextEditor is set
20 | string backupTextQueue = "";
21 | public string Text
22 | {
23 | get => TextEditor?.Text ?? backupTextQueue;
24 | private set
25 | {
26 | if(TextEditor == null)
27 | {
28 | backupTextQueue = value;
29 | }
30 | else if(value != TextEditor.Text)
31 | {
32 | var old = TextEditor.Text;
33 | TextEditor.Text = value;
34 | RaisePropertyChanged(TextProperty, old, Text);
35 | }
36 | }
37 | }
38 | #endregion
39 |
40 | TextEditor? TextEditor;
41 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
42 | {
43 | base.OnApplyTemplate(e);
44 | TextEditor = e.NameScope.Find(PART_TextEditor);
45 | TextEditor.TextChanged += (o, e) => this.RaisePropertyChanged(TextProperty, Text, Text);
46 | Text = backupTextQueue;
47 | backupTextQueue = "";
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/TileMouseHelper.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Input;
4 | using System;
5 | using System.Windows.Input;
6 |
7 | namespace TheKingsTable.Controls
8 | {
9 | public class TileEventArgs : EventArgs
10 | {
11 | public int X { get; }
12 | public int Y { get; }
13 | public PointerUpdateKind? Pressed { get; }
14 |
15 | public TileEventArgs(int x, int y, PointerUpdateKind? pressed = null)
16 | {
17 | X = x;
18 | Y = y;
19 | Pressed = pressed;
20 | }
21 | }
22 | public abstract class TileMouseHelper : Control
23 | {
24 | static protected readonly AvaloniaProperty[] Properties;
25 | static TileMouseHelper()
26 | {
27 | //needed to use the static constructor
28 | //to avoid complaints about null
29 | Properties = new AvaloniaProperty[]
30 | {
31 | TileSizeProperty,
32 | SelectionStartXProperty,
33 | SelectionStartYProperty,
34 | SelectionEndXProperty,
35 | SelectionEndYProperty,
36 | ScaleProperty
37 | };
38 | }
39 |
40 | #region TileSize Styled Property
41 | public static readonly StyledProperty TileSizeProperty =
42 | AvaloniaProperty.Register(nameof(TileSize), 16);
43 |
44 | public int TileSize
45 | {
46 | get => GetValue(TileSizeProperty);
47 | set => SetValue(TileSizeProperty, value);
48 | }
49 | #endregion
50 |
51 | #region SelectionStartX Styled Property
52 | public static readonly StyledProperty SelectionStartXProperty =
53 | AvaloniaProperty.Register(nameof(SelectionStartX), -1);
54 |
55 | public int SelectionStartX
56 | {
57 | get => GetValue(SelectionStartXProperty);
58 | set => SetValue(SelectionStartXProperty, value);
59 | }
60 | #endregion
61 |
62 | #region SelectionStartY Styled Property
63 | public static readonly StyledProperty SelectionStartYProperty =
64 | AvaloniaProperty.Register(nameof(SelectionStartY), -1);
65 |
66 | public int SelectionStartY
67 | {
68 | get => GetValue(SelectionStartYProperty);
69 | set => SetValue(SelectionStartYProperty, value);
70 | }
71 | #endregion
72 |
73 | #region SelectionEndX Styled Property
74 | public static readonly StyledProperty SelectionEndXProperty =
75 | AvaloniaProperty.Register(nameof(SelectionEndX), -1);
76 |
77 | public int SelectionEndX
78 | {
79 | get => GetValue(SelectionEndXProperty);
80 | set => SetValue(SelectionEndXProperty, value);
81 | }
82 | #endregion
83 |
84 | #region SelectionEndY Styled Property
85 | public static readonly StyledProperty SelectionEndYProperty =
86 | AvaloniaProperty.Register(nameof(SelectionEndY), -1);
87 |
88 | public int SelectionEndY
89 | {
90 | get => GetValue(SelectionEndYProperty);
91 | set => SetValue(SelectionEndYProperty, value);
92 | }
93 | #endregion
94 |
95 | #region Scale Styled Property
96 | public static readonly StyledProperty ScaleProperty =
97 | AvaloniaProperty.Register(nameof(Scale), 1);
98 |
99 | public double Scale
100 | {
101 | get => GetValue(ScaleProperty);
102 | set => SetValue(ScaleProperty, value);
103 | }
104 | #endregion
105 |
106 | protected double ZoomMin = 0.25, ZoomStep = 0.25, ZoomMax = 10;
107 |
108 | int TileX = -1, TileY = -1;
109 | TileEventArgs CurrentPos => new TileEventArgs(TileX, TileY);
110 | protected Rect GetTileRect(byte tile, int width)
111 | {
112 | return new Rect((tile % width) * TileSize, (tile / width) * TileSize, TileSize, TileSize);
113 | }
114 | protected (int, int) PointToTile(Point p)
115 | {
116 | return ((int)(p.X / (TileSize * Scale)),
117 | (int)(p.Y / (TileSize * Scale)));
118 | }
119 |
120 | public TileMouseHelper()
121 | {
122 | PointerEnter += OnPointerEnter;
123 | PointerMoved += OnPointerMoved;
124 | PointerWheelChanged += OnPointerWheelChanged;
125 |
126 | PointerPressed += OnPointerPressed;
127 | PointerReleased += OnPointerReleased;
128 |
129 | PointerLeave += OnPointerLeave;
130 | PointerCaptureLost += OnPointerCaptureLost;
131 |
132 | this.AddHandler(Control.KeyDownEvent, OnKeyDown);
133 | //KeyDown += OnKeyDown;
134 | Focusable = true;
135 | }
136 | ~TileMouseHelper()
137 | {
138 | PointerEnter -= OnPointerEnter;
139 | PointerMoved -= OnPointerMoved;
140 | PointerWheelChanged -= OnPointerWheelChanged;
141 |
142 | PointerPressed -= OnPointerPressed;
143 | PointerReleased -= OnPointerReleased;
144 |
145 | PointerLeave -= OnPointerLeave;
146 | PointerCaptureLost -= OnPointerCaptureLost;
147 |
148 | this.RemoveHandler(Control.KeyUpEvent, OnKeyDown);
149 | //KeyDown -= OnKeyDown;
150 | }
151 | public event EventHandler? PointerEnterTile, PointerWheelChangedTile, PointerMovedTile, PointerPressedTile, PointerReleasedTile;
152 | private void OnPointerEnter(object? sender, PointerEventArgs e)
153 | {
154 | (TileX, TileY) = PointToTile(e.GetPosition(this));
155 | PointerEnterTile?.Invoke(sender, CurrentPos);
156 | pointerEnterCommand?.Execute(CurrentPos);
157 | }
158 | protected bool StepZoom(double step)
159 | {
160 | if (step != 0)
161 | {
162 | if (step > 0)
163 | Scale += ZoomStep;
164 | else if (step < 0)
165 | Scale -= ZoomStep;
166 |
167 | if (Scale < ZoomMin)
168 | Scale = ZoomMin;
169 | else if (Scale > ZoomMax)
170 | Scale = ZoomMax;
171 |
172 | return true;
173 | }
174 | return false;
175 | }
176 | private void OnPointerWheelChanged(object? sender, PointerWheelEventArgs e)
177 | {
178 | if (e.KeyModifiers == KeyModifiers.Control)
179 | {
180 | e.Handled = StepZoom(e.Delta.Y);
181 | }
182 | }
183 | private void OnPointerMoved(object? sender, PointerEventArgs e)
184 | {
185 | (var x, var y) = PointToTile(e.GetPosition(this));
186 | if (x != TileX || y != TileY)
187 | {
188 | TileX = x;
189 | TileY = y;
190 | PointerMovedTile?.Invoke(sender, CurrentPos);
191 | pointerMovedCommand?.Execute(CurrentPos);
192 | }
193 | }
194 | private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
195 | {
196 | //certain circumstances (such as opening the context menu, then clicking again)
197 | //cause the cursor position to desync at this point, so we run the move code again just in case
198 | OnPointerMoved(this, e);
199 | var args = new TileEventArgs(TileX, TileY, e.GetCurrentPoint(this).Properties.PointerUpdateKind);
200 | PointerPressedTile?.Invoke(sender, args);
201 | pointerPressedCommand?.Execute(args);
202 | }
203 | private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
204 | {
205 | var args = new TileEventArgs(TileX, TileY, e.GetCurrentPoint(this).Properties.PointerUpdateKind);
206 | PointerReleasedTile?.Invoke(sender, args);
207 | pointerReleasedCommand?.Execute(args);
208 | }
209 | private void OnPointerLeave(object? sender, PointerEventArgs e)
210 | {
211 | TileX = -1;
212 | TileY = -1;
213 | pointerLeaveCommand?.Execute(null);
214 | }
215 | private void OnPointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
216 | {
217 | TileX = -1;
218 | TileY = -1;
219 | pointerCaptureLostCommand?.Execute(null);
220 | }
221 |
222 | #region PointerEnterCommand Direct Property
223 | public static readonly DirectProperty PointerEnterCommandProperty =
224 | AvaloniaProperty.RegisterDirect(nameof(PointerEnterCommand),
225 | o => o.PointerEnterCommand, (o, v) => o.PointerEnterCommand = v);
226 |
227 | ICommand? pointerEnterCommand;
228 | public ICommand? PointerEnterCommand
229 | {
230 | get => pointerEnterCommand;
231 | private set => SetAndRaise(PointerEnterCommandProperty, ref pointerEnterCommand, value);
232 | }
233 | #endregion
234 |
235 | #region PointerMovedCommand Direct Property
236 | public static readonly DirectProperty PointerMovedCommandProperty =
237 | AvaloniaProperty.RegisterDirect(nameof(PointerMovedCommand),
238 | o => o.PointerMovedCommand, (o, v) => o.PointerMovedCommand = v);
239 |
240 | ICommand? pointerMovedCommand;
241 | public ICommand? PointerMovedCommand
242 | {
243 | get => pointerMovedCommand;
244 | private set => SetAndRaise(PointerMovedCommandProperty, ref pointerMovedCommand, value);
245 | }
246 | #endregion
247 |
248 | #region PointerWheelChangedCommand Direct Property
249 | public static readonly DirectProperty PointerWheelChangedCommandProperty =
250 | AvaloniaProperty.RegisterDirect(nameof(PointerWheelChangedCommand),
251 | o => o.PointerWheelChangedCommand, (o, v) => o.PointerWheelChangedCommand = v);
252 |
253 | ICommand? pointerWheelChangedCommand;
254 | public ICommand? PointerWheelChangedCommand
255 | {
256 | get => pointerWheelChangedCommand;
257 | private set => SetAndRaise(PointerWheelChangedCommandProperty, ref pointerWheelChangedCommand, value);
258 | }
259 | #endregion
260 |
261 | #region PointerPressedCommand Direct Property
262 | public static readonly DirectProperty PointerPressedCommandProperty =
263 | AvaloniaProperty.RegisterDirect(nameof(PointerPressedCommand),
264 | o => o.PointerPressedCommand, (o, v) => o.PointerPressedCommand = v);
265 |
266 | ICommand? pointerPressedCommand;
267 | public ICommand? PointerPressedCommand
268 | {
269 | get => pointerPressedCommand;
270 | private set => SetAndRaise(PointerPressedCommandProperty, ref pointerPressedCommand, value);
271 | }
272 | #endregion
273 |
274 | #region PointerReleasedCommand Direct Property
275 | public static readonly DirectProperty PointerReleasedCommandProperty =
276 | AvaloniaProperty.RegisterDirect(nameof(PointerReleasedCommand),
277 | o => o.PointerReleasedCommand, (o, v) => o.PointerReleasedCommand = v);
278 |
279 | ICommand? pointerReleasedCommand;
280 | public ICommand? PointerReleasedCommand
281 | {
282 | get => pointerReleasedCommand;
283 | private set => SetAndRaise(PointerReleasedCommandProperty, ref pointerReleasedCommand, value);
284 | }
285 | #endregion
286 |
287 | #region PointerLeaveCommand Direct Property
288 | public static readonly DirectProperty PointerLeaveCommandProperty =
289 | AvaloniaProperty.RegisterDirect(nameof(PointerLeaveCommand),
290 | o => o.PointerLeaveCommand, (o, v) => o.PointerLeaveCommand = v);
291 |
292 | ICommand? pointerLeaveCommand;
293 | public ICommand? PointerLeaveCommand
294 | {
295 | get => pointerLeaveCommand;
296 | private set => SetAndRaise(PointerLeaveCommandProperty, ref pointerLeaveCommand, value);
297 | }
298 | #endregion
299 |
300 | #region PointerCaptureLostCommand Direct Property
301 | public static readonly DirectProperty PointerCaptureLostCommandProperty =
302 | AvaloniaProperty.RegisterDirect(nameof(PointerCaptureLostCommand),
303 | o => o.PointerCaptureLostCommand, (o, v) => o.PointerCaptureLostCommand = v);
304 |
305 | ICommand? pointerCaptureLostCommand;
306 | public ICommand? PointerCaptureLostCommand
307 | {
308 | get => pointerCaptureLostCommand;
309 | private set => SetAndRaise(PointerCaptureLostCommandProperty, ref pointerCaptureLostCommand, value);
310 | }
311 | #endregion
312 |
313 | private void OnKeyDown(object? sender, KeyEventArgs e)
314 | {
315 | if(e.KeyModifiers == KeyModifiers.Control)
316 | {
317 | switch (e.Key)
318 | {
319 | case Key.OemPlus:
320 | case Key.Add:
321 | e.Handled = StepZoom(1);
322 | break;
323 | case Key.OemMinus:
324 | case Key.Subtract:
325 | e.Handled = StepZoom(-1);
326 | break;
327 | }
328 | }
329 | keyDownCommand?.Execute(e);
330 | }
331 |
332 | #region KeyDownCommand Direct Property
333 | public static readonly DirectProperty KeyDownCommandProperty =
334 | AvaloniaProperty.RegisterDirect(nameof(KeyDownCommand),
335 | o => o.KeyDownCommand, (o,v) => o.KeyDownCommand = v);
336 |
337 | ICommand? keyDownCommand;
338 | public ICommand? KeyDownCommand
339 | {
340 | get => keyDownCommand;
341 | private set => SetAndRaise(KeyDownCommandProperty, ref keyDownCommand, value);
342 | }
343 | #endregion
344 | }
345 | }
346 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/TileSelectionEditor.axaml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
33 |
34 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/TileSelectionEditor.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using Avalonia.Media.Imaging;
6 | using CaveStoryModdingFramework.Editors;
7 | using CaveStoryModdingFramework.Maps;
8 | using static TheKingsTable.Utilities;
9 |
10 | namespace TheKingsTable.Controls
11 | {
12 | [TemplatePart(Name = PART_SelectionViewer, Type = typeof(StageRenderer))]
13 | [TemplatePart(Name = PART_TilesetViewer, Type = typeof(TilesetViewer))]
14 | public partial class TileSelectionEditor : TemplatedControl
15 | {
16 | public const string PART_SelectionViewer = nameof(PART_SelectionViewer);
17 | public const string PART_TilesetViewer = nameof(PART_TilesetViewer);
18 |
19 | #region ShowTileTypes Styled Property
20 | public static readonly StyledProperty ShowTileTypesProperty =
21 | AvaloniaProperty.Register(nameof(ShowTileTypes), false);
22 |
23 | public bool ShowTileTypes
24 | {
25 | get => GetValue(ShowTileTypesProperty);
26 | set => SetValue(ShowTileTypesProperty, value);
27 | }
28 | #endregion
29 |
30 | #region TheTileSelection Styled Property
31 | public static readonly StyledProperty TheTileSelectionProperty =
32 | AvaloniaProperty.Register(nameof(TheTileSelection), null);
33 |
34 | public TileSelection? TheTileSelection
35 | {
36 | get => GetValue(TheTileSelectionProperty);
37 | set => SetValue(TheTileSelectionProperty, value);
38 | }
39 | #endregion
40 |
41 | #region TileTypesImage Styled Property
42 | public static readonly StyledProperty TileTypesImageProperty =
43 | AvaloniaProperty.Register(nameof(TileTypesImage), null);
44 |
45 | public Bitmap? TileTypesImage
46 | {
47 | get => GetValue(TileTypesImageProperty);
48 | set => SetValue(TileTypesImageProperty, value);
49 | }
50 | #endregion
51 |
52 | #region TilesetAttributes Styled Property
53 | public static readonly StyledProperty TilesetAttributesProperty =
54 | AvaloniaProperty.Register(nameof(TilesetAttributes), null);
55 |
56 | public Attribute? TilesetAttributes
57 | {
58 | get => GetValue(TilesetAttributesProperty);
59 | set => SetValue(TilesetAttributesProperty, value);
60 | }
61 | #endregion
62 |
63 | #region TilesetImage Styled Property
64 | public static readonly StyledProperty TilesetImageProperty =
65 | AvaloniaProperty.Register(nameof(TilesetImage), null);
66 |
67 | public Bitmap? TilesetImage
68 | {
69 | get => GetValue(TilesetImageProperty);
70 | set => SetValue(TilesetImageProperty, value);
71 | }
72 | #endregion
73 |
74 | StageRenderer SelectionViewer;
75 | TilesetViewer TilesetView;
76 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
77 | {
78 | base.OnApplyTemplate(e);
79 | SelectionViewer = e.NameScope.Find(PART_SelectionViewer);
80 | TilesetView = e.NameScope.Find(PART_TilesetViewer);
81 |
82 | SelectionViewer.PointerPressedTile += SelectionViewer_PointerPressedTile;
83 |
84 | TilesetView.PointerPressedTile += TilesetView_PointerPressedTile;
85 | TilesetView.PointerMovedTile += TilesetView_PointerMovedTile;
86 | TilesetView.PointerReleasedTile += TilesetView_PointerReleasedTile;
87 | }
88 |
89 | private void SelectionViewer_PointerPressedTile(object? sender, TileEventArgs e)
90 | {
91 | if (TheTileSelection != null && e.Pressed == Avalonia.Input.PointerUpdateKind.LeftButtonPressed)
92 | {
93 | TheTileSelection.CursorX = e.X;
94 | TheTileSelection.CursorY = e.Y;
95 | }
96 | }
97 |
98 | bool selectionActive = false;
99 | private void TilesetView_PointerPressedTile(object? sender, TileEventArgs e)
100 | {
101 | if (e.Pressed == Avalonia.Input.PointerUpdateKind.LeftButtonPressed)
102 | {
103 | selectionActive = true;
104 | TilesetView.SelectionStartX = TilesetView.SelectionEndX = e.X;
105 | TilesetView.SelectionStartY = TilesetView.SelectionEndY = e.Y;
106 | }
107 | }
108 |
109 | private void TilesetView_PointerMovedTile(object? sender, TileEventArgs e)
110 | {
111 | if (selectionActive)
112 | {
113 | TilesetView.SelectionEndX = e.X;
114 | TilesetView.SelectionEndY = e.Y;
115 | }
116 | }
117 |
118 | private void TilesetView_PointerReleasedTile(object? sender, TileEventArgs e)
119 | {
120 | if (selectionActive)
121 | {
122 | var r = PointsToRect(TilesetView.SelectionStartX, TilesetView.SelectionStartY, TilesetView.SelectionEndX, TilesetView.SelectionEndY);
123 | var newSel = new Map((short)(r.Width+1), (short)(r.Height+1));
124 | int i = 0;
125 | for (int y = (int)r.Top; y < r.Bottom + 1; y++)
126 | {
127 | for (int x = (int)r.Left; x < r.Right + 1; x++)
128 | {
129 | newSel.Tiles[i++] = (byte)((y * 16) + x);
130 | }
131 | }
132 | /*
133 | TilesetView.Selection = new TileSelection(
134 | TilesetView.SelectionEndX - (int)r.Left,
135 | TilesetView.SelectionEndY - (int)r.Top,
136 | newSel);
137 | /*/
138 | TilesetView.Selection.Contents = newSel;
139 | TilesetView.Selection.CursorX = TilesetView.SelectionEndX - (int)r.Left;
140 | TilesetView.Selection.CursorY = TilesetView.SelectionEndY - (int)r.Top;
141 | //*/
142 | selectionActive = false;
143 | }
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/TheKingsTable/Controls/TilesetViewer.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Media;
4 | using Avalonia.Media.Imaging;
5 | using Avalonia.Styling;
6 | using CaveStoryModdingFramework.Editors;
7 | using System;
8 | using System.Linq;
9 |
10 | namespace TheKingsTable.Controls
11 | {
12 | public class TilesetViewer : TileMouseHelper, IStyleable
13 | {
14 | Type IStyleable.StyleKey => typeof(Control);
15 |
16 | static TilesetViewer()
17 | {
18 | AffectsRender(new AvaloniaProperty[] {
19 | TilesetImageProperty,
20 | TileTypesImageProperty,
21 | TilesetAttributesProperty,
22 | ShowTileTypesProperty,
23 | SelectionProperty
24 | }.Concat(Properties).ToArray());
25 | }
26 |
27 | #region TilesetImage Styled Property
28 | public static readonly StyledProperty TilesetImageProperty =
29 | AvaloniaProperty.Register(nameof(TilesetImage), null);
30 |
31 | public Bitmap? TilesetImage
32 | {
33 | get => GetValue(TilesetImageProperty);
34 | set => SetValue(TilesetImageProperty, value);
35 | }
36 | #endregion
37 |
38 | #region TileTypesImage Styled Property
39 | public static readonly StyledProperty TileTypesImageProperty =
40 | AvaloniaProperty.Register(nameof(TileTypesImage), null);
41 |
42 | public Bitmap? TileTypesImage
43 | {
44 | get => GetValue(TileTypesImageProperty);
45 | set => SetValue(TileTypesImageProperty, value);
46 | }
47 | #endregion
48 |
49 | #region TilesetAttributes Styled Property
50 | public static readonly StyledProperty TilesetAttributesProperty =
51 | AvaloniaProperty.Register(nameof(TilesetAttributes), null);
52 |
53 | public CaveStoryModdingFramework.Maps.Attribute? TilesetAttributes
54 | {
55 | get => GetValue(TilesetAttributesProperty);
56 | set => SetValue(TilesetAttributesProperty, value);
57 | }
58 | #endregion
59 |
60 | #region ShowTileTypes Styled Property
61 | public static readonly StyledProperty ShowTileTypesProperty =
62 | AvaloniaProperty.Register(nameof(ShowTileTypes), false);
63 |
64 | public bool ShowTileTypes
65 | {
66 | get => GetValue(ShowTileTypesProperty);
67 | set => SetValue(ShowTileTypesProperty, value);
68 | }
69 | #endregion
70 |
71 | #region Selection Styled Property
72 | public static readonly StyledProperty SelectionProperty =
73 | AvaloniaProperty.Register(nameof(Selection), null);
74 |
75 | public TileSelection Selection
76 | {
77 | get => GetValue(SelectionProperty);
78 | set => SetValue(SelectionProperty, value);
79 | }
80 | #endregion
81 |
82 | PixelSize BufferSize => new PixelSize(TileSize * 16, TileSize * 16);
83 | Rect MainRect => new Rect(new Point(0, 0), BufferSize.ToSize(1));
84 |
85 | public override void Render(DrawingContext context)
86 | {
87 | context.FillRectangle(Brushes.Black, MainRect);
88 | if (TilesetImage != null)
89 | {
90 | var src = new Rect(TilesetImage.Size);
91 | context.DrawImage(TilesetImage, src, MainRect.Intersect(src));
92 | }
93 | if (TilesetAttributes != null && TileTypesImage != null && ShowTileTypes)
94 | {
95 | for(int i = 0; i < TilesetAttributes.Tiles.Count; i++)
96 | context.DrawImage(TileTypesImage,
97 | GetTileRect(TilesetAttributes.Tiles[i],16)
98 | ,GetTileRect((byte)i,16));
99 | }
100 | if(Selection != null
101 | && Selection.Contents.Tiles.Count > 0
102 | && Selection.Contents.Tiles[0] != null)
103 | {
104 | //a valid selection to display in the tileset should be of the form
105 | //n, ..., n+x
106 | //n+(y*16), ..., n+(y*16)+x
107 | //so first we find n...
108 | var start = (byte)Selection.Contents.Tiles[0]!;
109 | int i = 0;
110 | for(int y = 0; y < Selection.Contents.Height; y++)
111 | {
112 | for(int x = 0; x < Selection.Contents.Width; x++)
113 | {
114 | //...then we check it meets the condition
115 | if (Selection.Contents.Tiles[i++] != start + (16 * y) + x)
116 | return;
117 | }
118 | }
119 | //if we made it to here, we can draw the selection box
120 | var sx = start % 16;
121 | var sy = start / 16;
122 | context.DrawRectangle(new Pen(Brushes.Gray),
123 | new Rect(sx * TileSize, sy * TileSize,
124 | (Selection.Contents.Width * TileSize) - 1,
125 | (Selection.Contents.Height * TileSize) - 1
126 | ));
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/TheKingsTable/Controls/WizardControl.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using Avalonia.Styling;
6 | using System;
7 | using System.Windows.Input;
8 |
9 | namespace TheKingsTable.Controls
10 | {
11 | [TemplatePart(Name = PART_Carousel, Type = typeof(Carousel))]
12 | [TemplatePart(Name = PART_BackButton, Type = typeof(Button))]
13 | [TemplatePart(Name = PART_NextButton, Type = typeof(Button))]
14 | public class WizardControl : Carousel, IStyleable
15 | {
16 | public const string PART_Carousel = nameof(PART_Carousel);
17 | public const string PART_BackButton = nameof(PART_BackButton);
18 | public const string PART_NextButton = nameof(PART_NextButton);
19 |
20 | Type IStyleable.StyleKey => typeof(WizardControl);
21 |
22 | //TODO I kinda wish these had default functions that just ++/--
23 | #region BackCommand Styled Property
24 | public static readonly StyledProperty BackCommandProperty =
25 | AvaloniaProperty.Register(nameof(BackCommand));
26 | public ICommand BackCommand
27 | {
28 | get => GetValue(BackCommandProperty);
29 | set => SetValue(BackCommandProperty, value);
30 | }
31 | #endregion
32 |
33 | #region NextCommand Styled Property
34 | public static readonly StyledProperty NextCommandProperty =
35 | AvaloniaProperty.Register(nameof(NextCommand));
36 | public ICommand NextCommand
37 | {
38 | get => GetValue(NextCommandProperty);
39 | set => SetValue(NextCommandProperty, value);
40 | }
41 | #endregion
42 |
43 | Carousel Carousel;
44 | Button BackButton, NextButton;
45 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
46 | {
47 | base.OnApplyTemplate(e);
48 | Carousel = e.NameScope.Find(PART_Carousel);
49 | BackButton = e.NameScope.Find