├── .editorconfig
├── .gitattributes
├── .gitignore
├── CodeEditor-WinUI-TestApp
├── CodeWriter-WinUI-TestApp (Package)
│ ├── CodeEditor-WinUI-TestApp (Package).wapproj
│ ├── Images
│ │ ├── LockScreenLogo.scale-200.png
│ │ ├── SplashScreen.scale-200.png
│ │ ├── Square150x150Logo.scale-200.png
│ │ ├── Square44x44Logo.scale-200.png
│ │ ├── Square44x44Logo.targetsize-24_altform-unplated.png
│ │ ├── StoreLogo.png
│ │ └── Wide310x150Logo.scale-200.png
│ └── Package.appxmanifest
└── CodeWriter-WinUI-TestApp
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── CodeEditor-WinUI-TestApp.csproj
│ ├── ExampleText.lua
│ ├── ExampleText.tex
│ ├── MainPage.xaml
│ ├── MainPage.xaml.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── SystemBackdropWindow.cs
│ ├── ViewModel.cs
│ └── app.manifest
├── CodeEditorControl-WinUI.sln
├── CodeEditorControl-WinUI
├── CodeEditorControl-WinUI.csproj
├── CodeWriter.Actions.xaml.cs
├── CodeWriter.DragDrop.xaml.cs
├── CodeWriter.Folding.xaml.cs
├── CodeWriter.IntelliSense.xaml.cs
├── CodeWriter.Properties.xaml.cs
├── CodeWriter.Scrolling.xaml.cs
├── CodeWriter.Search.xaml.cs
├── CodeWriter.Selection.xaml.cs
├── CodeWriter.xaml
├── CodeWriter.xaml.cs
├── Fonts
│ ├── FiraCode.ttf
│ └── JetBrainsMono.ttf
├── Models
│ ├── Converters.cs
│ ├── Editing.cs
│ ├── Enums.cs
│ ├── Helpers.cs
│ ├── IntelliSense.cs
│ ├── Language.cs
│ ├── Line.cs
│ ├── Range.cs
│ └── Search.cs
└── ScrollBarResourceDictionary.xaml
├── LICENSE
└── README.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # IDE0055: Formatierung beheben
4 | dotnet_diagnostic.IDE0055.severity = none
5 |
--------------------------------------------------------------------------------
/.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 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/CodeEditor-WinUI-TestApp (Package).wapproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 15.0
5 |
6 |
7 |
8 | Debug
9 | x86
10 |
11 |
12 | Release
13 | x86
14 |
15 |
16 | Debug
17 | x64
18 |
19 |
20 | Release
21 | x64
22 |
23 |
24 | Debug
25 | arm64
26 |
27 |
28 | Release
29 | arm64
30 |
31 |
32 |
33 | $(MSBuildExtensionsPath)\Microsoft\DesktopBridge\
34 | CodeWriter-WinUI-TestApp\
35 |
36 |
37 |
38 | 12dc5eb6-6c29-4758-9497-e3be00438b20
39 | 10.0.22000.0
40 | 10.0.17763.0
41 | net7.0-windows$(TargetPlatformVersion);$(AssetTargetFallback)
42 | en-US
43 | false
44 | ..\CodeWriter-WinUI-TestApp\CodeEditor-WinUI-TestApp.csproj
45 |
46 |
47 | en-US
48 |
49 |
50 | en-US
51 |
52 |
53 | en-US
54 |
55 |
56 | en-US
57 |
58 |
59 | en-US
60 |
61 |
62 | en-US
63 |
64 |
65 |
66 | Designer
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | True
81 | Properties\PublishProfiles\win10-$(Platform).pubxml
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/LockScreenLogo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/LockScreenLogo.scale-200.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/SplashScreen.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/SplashScreen.scale-200.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Square150x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Square150x150Logo.scale-200.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Square44x44Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Square44x44Logo.scale-200.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Square44x44Logo.targetsize-24_altform-unplated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Square44x44Logo.targetsize-24_altform-unplated.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/StoreLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/StoreLogo.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Wide310x150Logo.scale-200.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Images/Wide310x150Logo.scale-200.png
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp (Package)/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
13 |
14 |
15 | CodeEditor-WinUI-TestApp
16 | Welter
17 | Images\StoreLogo.png
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 |
3 |
4 | namespace CodeEditor_WinUI_TestApp
5 | {
6 | public partial class App : Application
7 | {
8 | public App()
9 | {
10 | InitializeComponent();
11 | }
12 |
13 | public static MainWindow MainWindow { get; set; }
14 |
15 | protected override void OnLaunched(LaunchActivatedEventArgs args)
16 | {
17 | MainWindow = new MainWindow();
18 | MainWindow.Title = "CodeWriter-WinUI";
19 |
20 | MainWindow.Activate();
21 | }
22 |
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/CodeEditor-WinUI-TestApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net7.0-windows10.0.22621.0
5 | 10.0.22621.41
6 | 10.0.17763.0
7 | CodeEditor_WinUI_TestApp
8 | app.manifest
9 | x86;x64;arm64
10 | win10-x86;win10-x64;win10-arm64
11 | true
12 | true
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Always
23 |
24 |
25 | Always
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | MSBuild:Compile
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/ExampleText.lua:
--------------------------------------------------------------------------------
1 | if not modules then modules = { } end modules['s-math-characters'] = {
2 | version = 1.001,
3 | comment = "companion to s-math-characters.mkiv",
4 | author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 | copyright = "PRAGMA ADE / ConTeXt Development Team",
6 | license = "see context related readme files"
7 | }
8 |
9 | -- This is one of the oldest cld files but I'm not going to clean it up.
10 |
11 | moduledata.math = moduledata.math or { }
12 | moduledata.math.characters = moduledata.math.characters or { }
13 |
14 | local concat = table.concat
15 | local lower = string.lower
16 | local utfchar = utf.char
17 | local round = math.round
18 |
19 | local context = context
20 |
21 | local fontdata = fonts.hashes.identifiers
22 | local chardata = characters.data
23 | local blocks = characters.blocks
24 |
25 | local no_description = "no description, private to font"
26 |
27 | local limited = true
28 | local fillinthegaps = true
29 | local upperlimit = 0x0007F
30 | local upperlimit = 0xF0000
31 |
32 | local f_unicode = string.formatters["%U"]
33 | local f_slot = string.formatters["%s/%0X"]
34 |
35 | function moduledata.math.characters.showlist(specification)
36 | specification = interfaces.checkedspecification(specification)
37 | local id = specification.number -- or specification.id
38 | local list = specification.list
39 | local showvirtual = specification.virtual == "all"
40 | local check = specification.check == "yes"
41 | if not id then
42 | id = font.current()
43 | end
44 | if list == "" then
45 | list = nil
46 | end
47 | local tfmdata = fontdata[id]
48 | local characters = tfmdata.characters
49 | local descriptions = tfmdata.descriptions
50 | local resources = tfmdata.resources
51 | local lookuptypes = resources.lookuptypes
52 | local virtual = tfmdata.properties.virtualized
53 | local names = { }
54 | local gaps = mathematics.gaps
55 | local sorted = { }
56 | if type(list) == "string" then
57 | sorted = utilities.parsers.settings_to_array(list)
58 | for i=1,#sorted do
59 | sorted[i] = tonumber(sorted[i])
60 | end
61 | elseif type(list) == "table" then
62 | sorted = list
63 | for i=1,#sorted do
64 | sorted[i] = tonumber(sorted[i])
65 | end
66 | elseif fillinthegaps then
67 | sorted = table.keys(characters)
68 | for k, v in next, gaps do
69 | if characters[v] then
70 | sorted[#sorted+1] = k
71 | end
72 | end
73 | table.sort(sorted)
74 | else
75 | sorted = table.sortedkeys(characters)
76 | end
77 | if virtual then
78 | local fonts = tfmdata.fonts
79 | for i=1,#fonts do
80 | local id = fonts[i].id
81 | local name = fontdata[id].properties.name
82 | names[i] = (name and file.basename(name)) or id
83 | end
84 | end
85 | if check then
86 | for k, v in table.sortedhash(blocks) do
87 | if v.math then
88 | local first = v.first
89 | local last = v.last
90 | local f, l = 0, 0
91 | if first and last then
92 | for unicode=first,last do
93 | local code = gaps[unicode] or unicode
94 | local char = characters[code]
95 | if char and not (char.commands and not showvirtual) then
96 | f = unicode
97 | break
98 | end
99 | end
100 | for unicode=last,first,-1 do
101 | local code = gaps[unicode] or unicode
102 | local char = characters[code]
103 | if char and not (char.commands and not showvirtual) then
104 | l = unicode
105 | break
106 | end
107 | end
108 | context.showmathcharacterssetrange(k,f,l)
109 | end
110 | end
111 | end
112 | else
113 | context.showmathcharactersstart()
114 | for _, unicode in next, sorted do
115 | if not limited or unicode < upperlimit then
116 | local code = gaps[unicode] or unicode
117 | local char = characters[code]
118 | local desc = descriptions[code]
119 | local info = chardata[code]
120 | if char then
121 | local commands = char.commands
122 | if commands and not showvirtual then
123 | -- skip
124 | else
125 | local next_sizes = char.next
126 | local v_variants = char.vert_variants
127 | local h_variants = char.horiz_variants
128 | local slookups = desc and desc.slookups
129 | local mlookups = desc and desc.mlookups
130 | local mathclass = info.mathclass
131 | local mathspec = info.mathspec
132 | local mathsymbol = info.mathsymbol
133 | local description = info.description or no_description
134 | context.showmathcharactersstartentry()
135 | context.showmathcharactersreference(f_unicode(unicode))
136 | context.showmathcharactersentryhexdectit(f_unicode(code),code,lower(description))
137 | context.showmathcharactersentrywdhtdpic(round(char.width or 0),round(char.height or 0),round(char.depth or 0),round(char.italic or 0))
138 | if virtual and commands then
139 | local t = { }
140 | for i=1,#commands do
141 | local ci = commands[i]
142 | if ci[1] == "slot" then
143 | local fnt, idx = ci[2], ci[3]
144 | t[#t+1] = f_slot(names[fnt] or fnt,idx)
145 | end
146 | end
147 | if #t > 0 then
148 | context.showmathcharactersentryresource(concat(t,", "))
149 | end
150 | end
151 | if mathclass or mathspec then
152 | context.showmathcharactersstartentryclassspec()
153 | if mathclass then
154 | context.showmathcharactersentryclassname(mathclass,info.mathname or "no name")
155 | end
156 | if mathspec then
157 | for i=1,#mathspec do
158 | local mi = mathspec[i]
159 | context.showmathcharactersentryclassname(mi.class,mi.name or "no name")
160 | end
161 | end
162 | context.showmathcharactersstopentryclassspec()
163 | end
164 | if mathsymbol then
165 | context.showmathcharactersentrysymbol(f_unicode(mathsymbol),mathsymbol)
166 | end
167 | if next_sizes then
168 | local n, done = 0, { }
169 | context.showmathcharactersstartnext()
170 | while next_sizes do
171 | n = n + 1
172 | if done[next_sizes] then
173 | context.showmathcharactersnextcycle(n)
174 | break
175 | else
176 | done[next_sizes] = true
177 | context.showmathcharactersnextentry(n,f_unicode(next_sizes),next_sizes)
178 | next_sizes = characters[next_sizes]
179 | v_variants = next_sizes.vert_variants or v_variants
180 | h_variants = next_sizes.horiz_variants or h_variants
181 | if next_sizes then
182 | next_sizes = next_sizes.next
183 | end
184 | end
185 | end
186 | context.showmathcharactersstopnext()
187 | if h_variants or v_variants then
188 | context.showmathcharactersbetweennextandvariants()
189 | end
190 | end
191 | if h_variants then
192 | context.showmathcharactersstarthvariants()
193 | for i=1,#h_variants do -- we might go top-down in the original
194 | local vi = h_variants[i]
195 | context.showmathcharactershvariantsentry(i,f_unicode(vi.glyph),vi.glyph)
196 | end
197 | context.showmathcharactersstophvariants()
198 | elseif v_variants then
199 | context.showmathcharactersstartvvariants()
200 | for i=1,#v_variants do
201 | local vi = v_variants[#v_variants-i+1]
202 | context.showmathcharactersvvariantsentry(i,f_unicode(vi.glyph),vi.glyph)
203 | end
204 | context.showmathcharactersstopvvariants()
205 | end
206 | if slookups or mlookups then
207 | local variants = { }
208 | if slookups then
209 | for lookupname, lookupdata in next, slookups do
210 | local lookuptype = lookuptypes[lookupname]
211 | if lookuptype == "substitution" then
212 | variants[lookupdata] = "sub"
213 | elseif lookuptype == "alternate" then
214 | for i=1,#lookupdata do
215 | variants[lookupdata[i]] = "alt"
216 | end
217 | end
218 | end
219 | end
220 | if mlookups then
221 | for lookupname, lookuplist in next, mlookups do
222 | local lookuptype = lookuptypes[lookupname]
223 | for i=1,#lookuplist do
224 | local lookupdata = lookuplist[i]
225 | local lookuptype = lookuptypes[lookupname]
226 | if lookuptype == "substitution" then
227 | variants[lookupdata] = "sub"
228 | elseif lookuptype == "alternate" then
229 | for i=1,#lookupdata do
230 | variants[lookupdata[i]] = "alt"
231 | end
232 | end
233 | end
234 | end
235 | end
236 | context.showmathcharactersstartlookupvariants()
237 | local i = 0
238 | for variant, lookuptype in table.sortedpairs(variants) do
239 | i = i + 1
240 | context.showmathcharacterslookupvariant(i,f_unicode(variant),variant,lookuptype)
241 | end
242 | context.showmathcharactersstoplookupvariants()
243 | end
244 | context.showmathcharactersstopentry()
245 | end
246 | end
247 | end
248 | end
249 | context.showmathcharactersstop()
250 | end
251 | end
252 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/ExampleText.tex:
--------------------------------------------------------------------------------
1 | % General Layout
2 | \setupcolors[state=start]
3 | \setuppapersize[A4][A4]
4 | \mainlanguage[en] % Header names
5 | \language[en] % Hyphenation
6 | \enableregime[utf]
7 | \setuplayout[topspace=2cm, backspace=2cm, header=24pt, footer=36pt, height=middle, width=middle]
8 | \setuppagenumbering[alternative=onesided, page=no]
9 |
10 | % Typography
11 | \setupwhitespace[medium]
12 | \setupindenting[no]
13 | \setupbodyfont[sans, 11pt]
14 | \definefontfeature[default][mode=node, kern=yes, bliga=yes, tlig=yes, ccmp=yes, language=dflt, protrusion=quality, expansion=quality]
15 | \setupfooter[style=\tf]
16 | \setupheader[style=\tf
17 |
18 | % Section blocks layout
19 | \definemakeup[title][doublesided=no, page=right, headerstate=empty, footerstate=empty, pagestate=start, style=bigbodyfont, align=middle]
20 | \definestructureconversionset[frontpart:pagenumber][][Romannumerals]
21 | \definestructureconversionset[bodypart:pagenumber][][numbers]
22 | \definestructureconversionset[appendix:pagenumber][][Romannumerals]
23 | \definestructureconversionset[backpart:pagenumber][][Romannumerals]
24 | \startsectionblockenvironment[frontpart]
25 | \setuphead[section][page=no]
26 | \setupfootertexts[]
27 | \setupfootertexts[][\userpagenumber][\userpagenumber][]
28 | \setupheadertexts[]
29 | \setupheadertexts[][][][]
30 | \stopsectionblockenvironment
31 | \startsectionblockenvironment[bodypart]
32 | \setupfootertexts[]
33 | \setupfootertexts[][\userpagenumber][\userpagenumber][]
34 | \setupheadertexts[{\getmarking[sectionnumber] \getmarking[section]}]
35 | \setupheadertexts[][][][]
36 | \savenumber[userpage]
37 | \resetnumber[userpage]
38 | \stopsectionblockenvironment
39 | \startsectionblockenvironment[appendix]
40 | \setuphead[section][conversion=A]
41 | \setuplabeltext[section=Appendix~]
42 | \setupfootertexts[]
43 | \setupfootertexts[][\userpagenumber][\userpagenumber][]
44 | \setupheadertexts[]
45 | \setupheadertexts[][][][]
46 | \restorenumber[userpage]
47 | \incrementnumber[userpage]
48 | \decrementnumber[userpage]
49 | \stopsectionblockenvironment
50 | \startsectionblockenvironment[backpart]
51 | \setupfootertexts[]
52 | \setupfootertexts[][][][]
53 | \setupheadertexts[]
54 | \setupheadertexts[][][][]
55 | \stopsectionblockenvironment
56 |
57 | % Float captions
58 | \setupcaptions[table][location=top]
59 | \setupcaptions[figure][location=bottom]
60 | \setupcaptions[headstyle=\bf]
61 |
62 | % PDF Output Adjustments
63 | \setupinteraction[state=start, focus=standard, style=, click=yes, color=, contrastcolor=]
64 | \placebookmarks[chapter,section,subsection,subject][force=yes]
65 |
66 | % TOC style
67 | \setuplist[part][width=0mm, distance=2mm, style=bold, aligntitle=no]
68 | \setuplist[chapter][label=yes, width=fit, distance=2mm, style=bold]
69 | \setuplist[section,subject][width=0mm, distance=2mm, margin=0mm, style=bold, alternative=b]
70 | \setuplist[subsection,subsubject][width=0mm, distance=2mm, margin=5mm, alternative=c]
71 | \setuplist[subsubsection,subsubsubject][width=0mm, distance=2mm, margin=10mm, alternative=c]
72 |
73 | % Referencing automation: \ref[Type:ReferenceName], somewhat equivalent to \autoref in LaTeX
74 | \definereferenceformat[refsec][style=\tf, text=section ]
75 | \definereferenceformat[refeq][style=\tf, text=equation ]
76 | \definereferenceformat[reffig][style=\tf, text=figure ]
77 | \definereferenceformat[reftab][style=\tf, text=table ]
78 | \def\ref[#1:#2]{\csname ref#1\endcsname[#1:#2]}
79 |
80 | % Heading setup
81 | \setuphead[chapter,title][style={\bfd}]
82 | \setuphead[section][style={\bfc}, before={\bigskip}, after={}, page=yes, header=empty]
83 | \setuphead[subject][style={\bfc}, before={\bigskip}, after={}, page=no, header=empty]
84 | \setuphead[subsection,subsubject][style={\bfb}, before={\bigskip}, after={}]
85 | \setuphead[subsubsection,subsubsubject][style={\bfa}, before={\bigskip}, after={}]
86 |
87 | % Meta information
88 | \setvariables[meta][title={Title}, subtitle={Subtitle}, author={Author}, date={\currentdate}]
89 | \setupinteraction[title=\getvariable{meta}{title}, subtitle=\getvariable{meta}{subtitle}, author=\getvariable{meta}{author}, date=\getvariable{meta}{date}]
90 |
91 | \starttext
92 |
93 | \startfrontmatter
94 | \starttitlemakeup
95 | {\bfd \getvariable{meta}{title}}
96 | \blank[3*medium]
97 | {\tfb \getvariable{meta}{subtitle}}
98 | \blank[3*medium]
99 | {\tfa \getvariable{meta}{author}}
100 | \blank[2*medium]
101 | {\tfa \getvariable{meta}{date}}
102 | \stoptitlemakeup
103 |
104 | \completecontent
105 | \stopfrontmatter
106 |
107 | \startbodymatter
108 | \startsection[title=Basic functions]
109 | \startsubsection[title=Itemze]
110 | \startitemize
111 | \item Item one
112 | \item Item two
113 | \stopitemize
114 |
115 | \stopsubsection
116 |
117 | \startsubsection[title=Tables]
118 | \startplacetable[location=here, reference=tab:exampletable, title={Example table}]
119 | \setupTABLE[r][each][style=\tfx\it, align=center]
120 | \bTABLE[split=repeat,option=stretch]
121 | \bTABLEhead
122 | \bTR
123 | \bTH head1 \eTH
124 | \bTH head2 \eTH
125 | \eTR
126 | \eTABLEhead
127 | \bTABLEbody
128 | \bTR \bTD One \eTD \bTD two \eTD \eTR
129 | \bTR \bTD One \eTD \bTD two \eTD \eTR
130 | \eTABLEbody
131 | \eTABLE
132 | \stopplacetable
133 |
134 | This is a reference to \ref[tab:exampletable].
135 | \stopsubsection
136 | \stopsection
137 |
138 | \startsection[title=Formulas]
139 | \placeformula
140 | \startsubformulas
141 | \startformula
142 | \startalign
143 | \NC \frac{dX}{dt} \NC = k \cdot A \cdot (X_{GG} - X) \NR[eq:dXdt]
144 | \NC \frac{dc}{dt} \NC = - \frac{v_0}{\varepsilon} \cdot \frac{dc}{dx} + \frac{1}{\varepsilon} \cdot \frac{dX}{dt} \NR[eq:dcdt]
145 | \stopalign
146 | \stopformula
147 | \stopsubformulas
148 | This is a reference to \ref[eq:dcdt].
149 | \stopsection
150 | \stopbodymatter
151 |
152 | \startappendices
153 | \startsection[title={Bibliography}]
154 | \placelistofpublications
155 | \stopsection
156 | \stopappendices
157 |
158 | \startbackmatter
159 | \startsection[title={The Backmatter}]
160 | ...
161 | \stopsection
162 | \stopbackmatter
163 |
164 | \stoptext
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using CodeEditorControl_WinUI;
2 | using Microsoft.UI.Xaml;
3 | using Microsoft.UI.Xaml.Controls;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Threading.Tasks;
10 |
11 | namespace CodeEditor_WinUI_TestApp
12 | {
13 | public sealed partial class MainPage : Page
14 | {
15 | private int actioncount = 0;
16 |
17 | public MainPage()
18 | {
19 | this.InitializeComponent();
20 | VM.Text = VM.LastSavedText = File.ReadAllText(Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "ExampleText.lua"));
21 | CW.Save();
22 | }
23 |
24 |
25 | private ViewModel VM { get; } = new ViewModel();
26 |
27 | private void Btn_Add_Click(object sender, RoutedEventArgs e)
28 | {
29 | actioncount++;
30 | var item = new MenuFlyoutItem() { XamlRoot = CW.XamlRoot, Text = $"Induced Action {actioncount}: Selection Info", Icon = new SymbolIcon(Symbol.Help) };
31 | item.Click += async (a, b) =>
32 | {
33 | await new ContentDialog() { XamlRoot = CW.XamlRoot, Content = "You Selected the following text:\n" + CW.SelectedText, PrimaryButtonText = "Close", DefaultButton = ContentDialogButton.Primary }.ShowAsync();
34 | };
35 | CW.Action_Add(item);
36 | }
37 |
38 | private void Btn_Load_Click(object sender, RoutedEventArgs e)
39 | {
40 | VM.Language = "ConTeXt";
41 | VM.Text = VM.LastSavedText = File.ReadAllText(Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "ExampleText.tex"));
42 |
43 | Btn_Save.Visibility = Visibility.Visible;
44 | Btn_Load.Content = "Reload Textfile";
45 | }
46 |
47 | private void Btn_Save_Click(object sender, RoutedEventArgs e)
48 | {
49 | File.WriteAllText(Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "ExampleText.tex"), VM.Text);
50 | VM.LastSavedText = VM.Text;
51 | CW.Save(); // CodeWriter.Save() does not save file contents, it only resets all (text change, warning & error) markers
52 | CW_TextChanged(null,null);
53 | }
54 |
55 | private void CW_ErrorOccured(object sender, ErrorEventArgs e)
56 | {
57 | Exception ex = e.GetException();
58 | VM.Log += $"Error {ex.StackTrace.Replace("\r", "").Replace("\n", " -> ")}: {ex.Message}\n";
59 | LogScroll.ScrollToVerticalOffset(LogScroll.ScrollableHeight);
60 | }
61 |
62 | private async void CW_TextChanged(object sender, PropertyChangedEventArgs e)
63 | {
64 | // Search for syntax errors and other stuff you want to inform the user about
65 | List errors = new();
66 | await Task.Run(() =>
67 | {
68 | foreach (Line line in CW.Lines)
69 | {
70 | if (line.LineText.Count(x => x == '[') != line.LineText.Count(x => x == ']'))
71 | errors.Add(new() { SyntaxErrorType = SyntaxErrorType.Error, iLine = line.LineNumber - 1, Title = "Unbalanced brackets" });
72 |
73 | if (line.LineText.Count() == 25)
74 | errors.Add(new() { SyntaxErrorType = SyntaxErrorType.Warning, iLine = line.LineNumber - 1, Title = "Warning", Description = "Line contains 25 characters. That's a no-no!" });
75 | }
76 | });
77 | CW.SyntaxErrors = errors;
78 | }
79 |
80 | private void ThemeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
81 | {
82 | App.MainWindow.RequestedTheme = Enum.Parse(e.AddedItems.First().ToString());
83 |
84 | //// Change the Colors depending on the Theme:
85 | //if (App.MainWindow.ActualTheme == ApplicationTheme.Light)
86 | //{
87 | // CW.Color_Background = Color.FromArgb(150, 220, 220, 224);
88 | // CW.Color_LeftBackground = Colors.Transparent; //Color.FromArgb(255, 230, 230, 230);
89 | //}
90 | //else
91 | //{
92 | // CW.Color_Background = Color.FromArgb(50, 150, 150, 206);
93 | // CW.Color_LeftBackground = Colors.Transparent; //Color.FromArgb(255, 25, 25, 25);
94 | //}
95 |
96 | App.MainWindow.SetBackdrop( SystemBackdropWindow.BackdropType.Mica);
97 | App.MainWindow.ResetColors();
98 | }
99 |
100 | private void CW_InfoMessage(object sender, string e)
101 | {
102 | VM.Log += $"Log | " + e + "\n";
103 | LogScroll.ScrollToVerticalOffset(LogScroll.ScrollableHeight);
104 | }
105 | }
106 | }
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI;
2 | using Microsoft.UI.Windowing;
3 | using Microsoft.UI.Xaml;
4 | using Microsoft.UI.Xaml.Controls;
5 | using Microsoft.UI.Xaml.Controls.Primitives;
6 | using Microsoft.UI.Xaml.Data;
7 | using Microsoft.UI.Xaml.Input;
8 | using Microsoft.UI.Xaml.Media;
9 | using Microsoft.UI.Xaml.Media.Animation;
10 | using Microsoft.UI.Xaml.Navigation;
11 | using System;
12 | using System.Collections.Generic;
13 | using System.IO;
14 | using System.Linq;
15 | using System.Runtime.InteropServices.WindowsRuntime;
16 | using Windows.Foundation;
17 | using Windows.Foundation.Collections;
18 | using Windows.UI;
19 | using WindowId = Microsoft.UI.WindowId;
20 |
21 | // To learn more about WinUI, the WinUI project structure,
22 | // and more about our project templates, see: http://aka.ms/winui-project-info.
23 |
24 | namespace CodeEditor_WinUI_TestApp
25 | {
26 | ///
27 | /// An empty window that can be used on its own or navigated to within a Frame.
28 | ///
29 | public sealed partial class MainWindow : SystemBackdropWindow
30 | {
31 | public MainWindow()
32 | {
33 | this.InitializeComponent();
34 | IsCustomizationSupported = AppWindowTitleBar.IsCustomizationSupported();
35 | AW = GetAppWindowForCurrentWindow();
36 | AW.Title = "CodeEditorControl TestApp";
37 |
38 | if (IsCustomizationSupported)
39 | {
40 | AW.TitleBar.ExtendsContentIntoTitleBar = true;
41 | CustomDragRegion.Height = 32;
42 | }
43 | else
44 | {
45 | CustomDragRegion.BackgroundTransition = null;
46 | CustomDragRegion.Background = null;
47 | ExtendsContentIntoTitleBar = true;
48 | CustomDragRegion.Height = 28;
49 | SetTitleBar(CustomDragRegion);
50 | Title = "ConTeXt IDE";
51 | }
52 | ResetColors();
53 |
54 | }
55 | public void ResetColors()
56 | {
57 | if (IsCustomizationSupported)
58 | {
59 | AW.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
60 | AW.TitleBar.ButtonBackgroundColor = Colors.Transparent;
61 | AW.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(50, 125, 125, 125) ;
62 | AW.TitleBar.ButtonHoverForegroundColor = ActualTheme == ApplicationTheme.Light ? Colors.Black : Colors.White;
63 | AW.TitleBar.ButtonForegroundColor = ActualTheme == ApplicationTheme.Light ? Colors.Black : Colors.White;
64 | AW.TitleBar.ButtonInactiveForegroundColor = ActualTheme == ApplicationTheme.Light ? Color.FromArgb(255, 50, 50, 50) : Color.FromArgb(255, 200, 200, 200);
65 | }
66 | else
67 | {
68 | //Application.Current.Resources["WindowCaptionBackground"] = ...;
69 | //Application.Current.Resources["WindowCaptionBackgroundDisabled"] = ...;
70 | }
71 | }
72 |
73 | public AppWindow AW { get; set; }
74 | public IntPtr hWnd;
75 | public bool IsCustomizationSupported { get; set; } = false;
76 | private void RootFrame_Loaded(object sender, RoutedEventArgs e)
77 | {
78 | (sender as Frame).Navigate(typeof(MainPage),null,new EntranceNavigationTransitionInfo());
79 | }
80 | private AppWindow GetAppWindowForCurrentWindow()
81 | {
82 | hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
83 | WindowId myWndId = Win32Interop.GetWindowIdFromWindow(hWnd);
84 | return AppWindow.GetFromWindowId(myWndId);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/SystemBackdropWindow.cs:
--------------------------------------------------------------------------------
1 |
2 | using Microsoft.UI;
3 | using Microsoft.UI.Composition;
4 | using Microsoft.UI.Composition.SystemBackdrops;
5 | using Microsoft.UI.Xaml;
6 | using System.Runtime.InteropServices;
7 | using Windows.UI;
8 | using WinRT;
9 |
10 | namespace CodeEditor_WinUI_TestApp
11 | {
12 | public class SystemBackdropWindow : Window
13 | {
14 | public ApplicationTheme ActualTheme = ApplicationTheme.Dark;
15 | ElementTheme requestedTheme = ElementTheme.Default;
16 | public ElementTheme RequestedTheme
17 | {
18 | get => requestedTheme;
19 | set
20 | {
21 | requestedTheme = value;
22 |
23 | switch (value)
24 | {
25 | case ElementTheme.Dark: ActualTheme = ApplicationTheme.Dark; break;
26 | case ElementTheme.Light: ActualTheme = ApplicationTheme.Light; break;
27 | case ElementTheme.Default:
28 | var uiSettings = new Windows.UI.ViewManagement.UISettings();
29 | var defaultthemecolor = uiSettings.GetColorValue(Windows.UI.ViewManagement.UIColorType.Background);
30 | ActualTheme = defaultthemecolor == Colors.Black ? ApplicationTheme.Dark : ApplicationTheme.Light;
31 | break;
32 | }
33 | }
34 | }
35 |
36 | public SystemBackdropWindow() : base()
37 | {
38 |
39 | RequestedTheme = App.Current.RequestedTheme == ApplicationTheme.Dark ? ElementTheme.Dark : ElementTheme.Light;
40 |
41 | m_wsdqHelper = new WindowsSystemDispatcherQueueHelper();
42 | m_wsdqHelper.EnsureWindowsSystemDispatcherQueueController();
43 |
44 | SetBackdrop(BackdropType.Mica);
45 | }
46 |
47 | public enum BackdropType
48 | {
49 | Mica,
50 | Acrylic,
51 | Color,
52 | }
53 |
54 | WindowsSystemDispatcherQueueHelper m_wsdqHelper;
55 | BackdropType m_currentBackdrop;
56 | MicaController m_micaController;
57 | DesktopAcrylicController m_acrylicController;
58 | SystemBackdropConfiguration m_configurationSource;
59 |
60 | public void SetBackdrop(BackdropType type)
61 | {
62 | m_currentBackdrop = BackdropType.Color;
63 | if (m_micaController != null)
64 | {
65 | m_micaController.Dispose();
66 | m_micaController = null;
67 | }
68 | if (m_acrylicController != null)
69 | {
70 | m_acrylicController.Dispose();
71 | m_acrylicController = null;
72 | }
73 | this.Activated -= Window_Activated;
74 | this.Closed -= Window_Closed;
75 | m_configurationSource = null;
76 |
77 | if (type == BackdropType.Mica)
78 | {
79 | if (TrySetMicaBackdrop())
80 | {
81 | m_currentBackdrop = type;
82 | }
83 | else
84 | {
85 | type = BackdropType.Acrylic;
86 | }
87 | }
88 | if (type == BackdropType.Acrylic)
89 | {
90 | if (TrySetAcrylicBackdrop())
91 | {
92 | m_currentBackdrop = type;
93 | }
94 | else
95 | {
96 | }
97 | }
98 | }
99 |
100 | bool TrySetMicaBackdrop()
101 | {
102 | if (MicaController.IsSupported())
103 | {
104 | m_configurationSource = new SystemBackdropConfiguration();
105 | this.Activated += Window_Activated;
106 | this.Closed += Window_Closed;
107 |
108 | m_configurationSource.IsInputActive = true;
109 | switch (RequestedTheme)
110 | {
111 | case ElementTheme.Dark: m_configurationSource.Theme = SystemBackdropTheme.Dark; break;
112 | case ElementTheme.Light: m_configurationSource.Theme = SystemBackdropTheme.Light; break;
113 | case ElementTheme.Default: m_configurationSource.Theme = SystemBackdropTheme.Default; break;
114 | }
115 |
116 | m_micaController = new MicaController() { };
117 | m_micaController.AddSystemBackdropTarget(this.As());
118 | m_micaController.SetSystemBackdropConfiguration(m_configurationSource);
119 | return true;
120 | }
121 |
122 | return false;
123 | }
124 |
125 | bool TrySetAcrylicBackdrop()
126 | {
127 | if (DesktopAcrylicController.IsSupported())
128 | {
129 | m_configurationSource = new SystemBackdropConfiguration();
130 | this.Activated += Window_Activated;
131 | this.Closed += Window_Closed;
132 |
133 | m_configurationSource.IsInputActive = true;
134 |
135 | Color AcrylicColor = Colors.Transparent;
136 |
137 | switch (ActualTheme)
138 | {
139 | case ApplicationTheme.Dark: m_configurationSource.Theme = SystemBackdropTheme.Dark; AcrylicColor = Color.FromArgb(255,10,10,10); break;
140 | case ApplicationTheme.Light: m_configurationSource.Theme = SystemBackdropTheme.Light; AcrylicColor = Color.FromArgb(255, 200, 200, 210); break;
141 | }
142 |
143 | m_acrylicController = new() { TintColor = AcrylicColor, FallbackColor = AcrylicColor, TintOpacity = 0.9f, LuminosityOpacity = 0.8f };
144 | m_acrylicController.AddSystemBackdropTarget(this.As());
145 | m_acrylicController.SetSystemBackdropConfiguration(m_configurationSource);
146 | return true;
147 | }
148 |
149 | return false;
150 | }
151 |
152 | private void Window_Activated(object sender, WindowActivatedEventArgs args)
153 | {
154 | m_configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
155 | }
156 |
157 | private void Window_Closed(object sender, WindowEventArgs args)
158 | {
159 | if (m_micaController != null)
160 | {
161 | m_micaController.Dispose();
162 | m_micaController = null;
163 | }
164 | if (m_acrylicController != null)
165 | {
166 | m_acrylicController.Dispose();
167 | m_acrylicController = null;
168 | }
169 | this.Activated -= Window_Activated;
170 | m_configurationSource = null;
171 | }
172 | }
173 |
174 | class WindowsSystemDispatcherQueueHelper
175 | {
176 | [StructLayout(LayoutKind.Sequential)]
177 | struct DispatcherQueueOptions
178 | {
179 | internal int dwSize;
180 | internal int threadType;
181 | internal int apartmentType;
182 | }
183 |
184 | [DllImport("CoreMessaging.dll")]
185 | private static extern int CreateDispatcherQueueController([In] DispatcherQueueOptions options, [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object dispatcherQueueController);
186 |
187 | object m_dispatcherQueueController = null;
188 | public void EnsureWindowsSystemDispatcherQueueController()
189 | {
190 | if (Windows.System.DispatcherQueue.GetForCurrentThread() != null)
191 | {
192 | return;
193 | }
194 |
195 | if (m_dispatcherQueueController == null)
196 | {
197 | DispatcherQueueOptions options;
198 | options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
199 | options.threadType = 2;
200 | options.apartmentType = 2;
201 |
202 | CreateDispatcherQueueController(options, ref m_dispatcherQueueController);
203 | }
204 | }
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/ViewModel.cs:
--------------------------------------------------------------------------------
1 | using CodeEditorControl_WinUI;
2 | using Microsoft.UI.Xaml;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Collections.ObjectModel;
6 | using System.Linq;
7 | using Windows.UI;
8 |
9 | namespace CodeEditor_WinUI_TestApp
10 | {
11 | internal class ViewModel : Bindable
12 | {
13 | public static List LanguageList = new()
14 | {
15 | new("ConTeXt")
16 | {
17 | EnableIntelliSense = true,
18 | LineComment = "%",
19 | EscapeSymbols = new [] { '\\' },
20 | AutoClosingPairs = new() { { '[', ']' }, { '{', '}' }, },
21 | WordSelectionDefinitions = new() { /*language=regex*/ @"\b\w+?\b", /*language=regex*/ @"\\.+?\b" },
22 | RegexTokens = new()
23 | {
24 | { Token.Key, /*language=regex*/ @"(\w+?\s*?)(=)" },
25 | { Token.Math, /*language=regex*/ @"\${1,2}.*?\${1,2}" },
26 | { Token.Symbol, /*language=regex*/ @"[:=,.!?&+\-*\/\^~#;<>]" },
27 | { Token.Command, /*language=regex*/ @"\\.+?\b" },
28 | { Token.Function, /*language=regex*/ @"\\(define|place|enable|setup).+?\b" },
29 | { Token.Style, /*language=regex*/ @"\\(tf|bf|it|sl|bi|bs|sc)(x|xx|[a-e])?\b|(\\tt|\\ss|\\rm)\b" },
30 | { Token.Array, /*language=regex*/ @"\\\\|\\(b|e)(T)(C|Ds?|H|N|Rs?|X|Y)\b|(\\AR|\\DR|\\DC|\\DL|\\NI|\\NR|\\NC|\\HL|\\VL|\\FR|\\MR|\\LR|\\SR|\\TB|\\NB|\\NN|\\FL|\\ML|\\LL|\\TL|\\BL)\b" },
31 | { Token.Environment, /*language=regex*/ @"\\(start|stop).+?\b" },
32 | { Token.Reference, /*language=regex*/ @"(\#+?\d+|\w+?)(:(\#+?\d+|\w+?)\b)+|\\ref|\#+?\d+?\b" },
33 | { Token.Bracket, /*language=regex*/ @"(?,.!?&%+\|\-*\/\^~;]" },
64 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
65 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*|-?\d*\.\d+([eE][\-+]?\d+)?|-?\d+?" },
66 | { Token.String, /*language=regex*/"\\\".*?\\\"|'.*?'" },
67 | { Token.Comment, /*language=regex*/"\\\"[^\\\"]*\\\" | --.*?\\\n" },
68 | },
69 | WordTokens = new()
70 | {
71 | { Token.Keyword, new string[] { "local", "true", "false", "in", "else", "not", "or", "and", "then", "nil", "end", "do", "repeat", "goto", "until", "return", "break" } },
72 | { Token.Environment, new string[] { "function", "end", "if", "elseif", "else", "while", "for", } },
73 | { Token.Function, new string[] { "#", "assert", "collectgarbage", "dofile", "_G", "getfenv", "ipairs", "load", "loadstring", "pairs", "pcall", "print", "rawequal", "rawget", "rawset", "select", "setfenv", "_VERSION", "xpcall", "module", "require", "tostring", "tonumber", "type", "rawset", "setmetatable", "getmetatable", "error", "unpack", "next", } }
74 | },
75 | },
76 | new("Log")
77 | {
78 | FoldingPairs = new()
79 | {
80 |
81 | },
82 | RegexTokens = new()
83 | {
84 | { Token.Keyword, /*language=regex*/ @"^[\w ]*?(?=>)" },
85 | { Token.Command, /*language=regex*/ @"\\.+?\b" },
86 | { Token.Symbol, /*language=regex*/ @"[:=<>,.!?&%+\|\-*\/\^~;]" },
87 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
88 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*|-?\d*\.\d+([eE][\-+]?\d+)?|-?\d+?" },
89 | },
90 | WordTokens = new()
91 | {
92 |
93 | },
94 | },
95 | new("Markdown")
96 | {
97 | FoldingPairs = new()
98 | {
99 |
100 | },
101 | RegexTokens = new()
102 | {
103 | { Token.Environment, /*language=regex*/ @"^\s*?#+? .*" },
104 | { Token.Keyword, /*language=regex*/ @"^[\w ]*?(?=>)" },
105 | { Token.Command, /*language=regex*/ @"(?<=<\/|<)\w+?\b(?=.*?\/?>)" },
106 | { Token.Function, /*language=regex*/ @"\[.*?\]" },
107 | { Token.Key, /*language=regex*/ @"(?<=\s)\w+?\s*?(?==)" },
108 | { Token.Comment, /*language=regex*/ @"^\s*?> .*" },
109 | { Token.String, /*language=regex*/ @"'.*?'" },
110 | { Token.Symbol, /*language=regex*/ @"[:=<>,.!?&%+\|\-*\/\^~;´`]" },
111 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
112 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*\b|-?\d*\.\d+([eE][\-+]?\d+)?\b|-?\d+?\b" },
113 |
114 | },
115 | WordTokens = new()
116 | {
117 |
118 | },
119 | },
120 | new("Xml")
121 | {
122 | FoldingPairs = new()
123 | {
124 |
125 | },
126 | RegexTokens = new()
127 | {
128 | { Token.Command, /*language=regex*/ @"?.+?/?>" },
129 | { Token.String, /*language=regex*/"\\\".*?\\\"|'.*?'" },
130 | { Token.Symbol, /*language=regex*/ @"[:=<>,.!?&%+\|\-*\/\^~;´`]" },
131 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
132 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*|-?\d*\.\d+([eE][\-+]?\d+)?|-?\d+?" },
133 | },
134 | WordTokens = new()
135 | {
136 |
137 | },
138 | },
139 | new("Text")
140 | {
141 | FoldingPairs = new()
142 | {
143 |
144 | },
145 | RegexTokens = new()
146 | {
147 |
148 | },
149 | WordTokens = new()
150 | {
151 |
152 | },
153 | },
154 | };
155 |
156 | public Language EditorLanguage { get => Get(LanguageList.First(x => x.Name == Language)); set { Set(value); } }
157 | public int FontSize { get => Get(20); set => Set(value); }
158 | public bool IsFoldingEnabled { get => Get(true); set => Set(value); }
159 | public bool IsWrappingEnabled { get => Get(false); set => Set(value); }
160 | public string Language { get => Get("Lua"); set { Set(value); EditorLanguage = LanguageList.First(x => x.Name == value); } }
161 | public string[] LanguageOptions => LanguageList.Select(x => x.Name).ToArray();
162 | public string LastSavedText { get => Get(""); set { Set(value); UnsavedChanges = value != Text; } }
163 | public string Log { get => Get(""); set => Set(value); }
164 | public ElementTheme RequestedTheme { get => Get(ElementTheme.Default); set => Set(value); }
165 | public bool ShowControlCharacters { get => Get(false); set => Set(value); }
166 | public bool ShowHorizontalTicks { get => Get(false); set => Set(value); }
167 | public bool ShowLineMarkers { get => Get(true); set => Set(value); }
168 | public bool ShowLineNumbers { get => Get(true); set => Set(value); }
169 | public bool ShowScrollbarMarkers { get => Get(true); set => Set(value); }
170 | public int TabLength { get => Get(2); set => Set(value); }
171 | public string Text { get => Get(""); set { Set(value); UnsavedChanges = value != LastSavedText; } }
172 | public string Theme { get => Get("Default"); set { Set(value); RequestedTheme = (ElementTheme)Enum.Parse(typeof(ElementTheme), value); } }
173 | public string Font { get => Get("Consolas"); set => Set(value); }
174 |
175 | public IndentGuide ShowIndentGuides { get => Get(IndentGuide.None); set => Set(value); }
176 | public string ShowIndentGuidesOption { get => Get("None"); set { Set(value); ShowIndentGuides = (IndentGuide)Enum.Parse(typeof(IndentGuide), value); } }
177 | public string[] ShowIndentGuidesOptions => Enum.GetNames();
178 | public string[] ThemeOptions => Enum.GetNames();
179 | public string[] FontOptions => new[] { "Consolas", "Courier New", "Lucida Sans Typewriter", "Cascadia Code", "Cascadia Mono", "Fira Code", "JetBrains Mono" };
180 | public bool UnsavedChanges { get => Get(false); set => Set(value); }
181 |
182 | public ObservableCollection TokenColorDefinitions
183 | {
184 | get => Get(new ObservableCollection(EditorOptions.TokenColors.Select(x => new TokenDefinition() { Token = x.Key, Color = x.Value })));
185 | set => Set(value);
186 | }
187 | }
188 | }
--------------------------------------------------------------------------------
/CodeEditor-WinUI-TestApp/CodeWriter-WinUI-TestApp/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31815.197
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeEditor-WinUI-TestApp", "CodeEditor-WinUI-TestApp\CodeWriter-WinUI-TestApp\CodeEditor-WinUI-TestApp.csproj", "{590FE7C5-69E9-4159-BB7F-703C9D070459}"
7 | EndProject
8 | Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "CodeEditor-WinUI-TestApp (Package)", "CodeEditor-WinUI-TestApp\CodeWriter-WinUI-TestApp (Package)\CodeEditor-WinUI-TestApp (Package).wapproj", "{12DC5EB6-6C29-4758-9497-E3BE00438B20}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CodeEditorControl-WinUI", "CodeEditorControl-WinUI\CodeEditorControl-WinUI.csproj", "{F53921CF-7A8F-4900-9A03-374902176218}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Debug|ARM64 = Debug|ARM64
16 | Debug|x64 = Debug|x64
17 | Debug|x86 = Debug|x86
18 | Release|Any CPU = Release|Any CPU
19 | Release|ARM64 = Release|ARM64
20 | Release|x64 = Release|x64
21 | Release|x86 = Release|x86
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|Any CPU.ActiveCfg = Debug|x86
25 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|Any CPU.Build.0 = Debug|x86
26 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|ARM64.ActiveCfg = Debug|arm64
27 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|ARM64.Build.0 = Debug|arm64
28 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|x64.ActiveCfg = Debug|x64
29 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|x64.Build.0 = Debug|x64
30 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|x86.ActiveCfg = Debug|x86
31 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Debug|x86.Build.0 = Debug|x86
32 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|Any CPU.ActiveCfg = Release|x86
33 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|ARM64.ActiveCfg = Release|arm64
34 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|ARM64.Build.0 = Release|arm64
35 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|x64.ActiveCfg = Release|x64
36 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|x64.Build.0 = Release|x64
37 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|x86.ActiveCfg = Release|x86
38 | {590FE7C5-69E9-4159-BB7F-703C9D070459}.Release|x86.Build.0 = Release|x86
39 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|Any CPU.ActiveCfg = Debug|x86
40 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|Any CPU.Build.0 = Debug|x86
41 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|Any CPU.Deploy.0 = Debug|x86
42 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|ARM64.ActiveCfg = Debug|arm64
43 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|ARM64.Build.0 = Debug|arm64
44 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|ARM64.Deploy.0 = Debug|arm64
45 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|x64.ActiveCfg = Debug|x64
46 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|x64.Build.0 = Debug|x64
47 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|x64.Deploy.0 = Debug|x64
48 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|x86.ActiveCfg = Debug|x86
49 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|x86.Build.0 = Debug|x86
50 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Debug|x86.Deploy.0 = Debug|x86
51 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|Any CPU.ActiveCfg = Release|x86
52 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|ARM64.ActiveCfg = Release|arm64
53 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|ARM64.Build.0 = Release|arm64
54 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|ARM64.Deploy.0 = Release|arm64
55 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|x64.ActiveCfg = Release|x64
56 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|x64.Build.0 = Release|x64
57 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|x64.Deploy.0 = Release|x64
58 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|x86.ActiveCfg = Release|x86
59 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|x86.Build.0 = Release|x86
60 | {12DC5EB6-6C29-4758-9497-E3BE00438B20}.Release|x86.Deploy.0 = Release|x86
61 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|ARM64.ActiveCfg = Debug|ARM64
64 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|ARM64.Build.0 = Debug|ARM64
65 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|x64.ActiveCfg = Debug|x64
66 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|x64.Build.0 = Debug|x64
67 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|x86.ActiveCfg = Debug|x86
68 | {F53921CF-7A8F-4900-9A03-374902176218}.Debug|x86.Build.0 = Debug|x86
69 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|Any CPU.ActiveCfg = Release|Any CPU
70 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|Any CPU.Build.0 = Release|Any CPU
71 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|ARM64.ActiveCfg = Release|ARM64
72 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|ARM64.Build.0 = Release|ARM64
73 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|x64.ActiveCfg = Release|x64
74 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|x64.Build.0 = Release|x64
75 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|x86.ActiveCfg = Release|Any CPU
76 | {F53921CF-7A8F-4900-9A03-374902176218}.Release|x86.Build.0 = Release|Any CPU
77 | EndGlobalSection
78 | GlobalSection(SolutionProperties) = preSolution
79 | HideSolutionNode = FALSE
80 | EndGlobalSection
81 | GlobalSection(ExtensibilityGlobals) = postSolution
82 | SolutionGuid = {FFFB2DD4-03C8-44B6-ABCF-11BAFB6987FC}
83 | EndGlobalSection
84 | EndGlobal
85 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeEditorControl-WinUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows10.0.22621.0
4 | 10.0.22621.38
5 | 10.0.17763.0
6 | x86;x64;ARM64
7 | CodeEditorControl_WinUI
8 | true
9 |
10 |
11 | False
12 | 2
13 | CS1998;CS4014
14 |
15 |
16 | False
17 | 2
18 | CS1998;CS4014
19 |
20 |
21 | False
22 | 2
23 | CS1998;CS4014
24 |
25 |
26 | 2
27 | CS1998;CS4014
28 |
29 |
30 | 2
31 | CS1998;CS4014
32 |
33 |
34 | 2
35 | CS1998;CS4014
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Always
45 |
46 |
47 | Always
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | CodeWriter.xaml
64 |
65 |
66 |
67 |
68 |
69 | MSBuild:Compile
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.Actions.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using System;
4 | using System.Collections.ObjectModel;
5 | using System.Collections.Specialized;
6 | using System.ComponentModel;
7 | using System.IO;
8 | using System.Linq;
9 | using Windows.ApplicationModel.DataTransfer;
10 | using Windows.ApplicationModel.DataTransfer.DragDrop;
11 |
12 | namespace CodeEditorControl_WinUI;
13 |
14 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
15 | {
16 | public bool CanUndo { get => Get(false); set => Set(value); }
17 | public bool CanToggleComment { get => Get(false); set => Set(value); }
18 | public bool CanRedo { get => Get(false); set => Set(value); }
19 |
20 | public ObservableCollection CursorPlaceHistory = new();
21 | public ObservableCollection EditActionHistory
22 | {
23 | get => (ObservableCollection)GetValue(EditActionHistoryProperty);
24 | set
25 | {
26 | SetValue(EditActionHistoryProperty, value);
27 | value.CollectionChanged += EditActionHistory_CollectionChanged;
28 | }
29 | }
30 | public ObservableCollection InvertedEditActionHistory { get => Get(new ObservableCollection()); set => Set(value); }
31 |
32 | public void Action_Add(MenuFlyoutItemBase item)
33 | {
34 | ContextMenu.Items.Add(item);
35 | }
36 |
37 | public void Action_Add(ICommandBarElement item)
38 | {
39 | //ContextMenu.SecondaryCommands.Add(item);
40 | }
41 |
42 | public void Action_Remove(MenuFlyoutItemBase item)
43 | {
44 | ContextMenu.Items.Remove(item);
45 | }
46 |
47 | public void Action_Remove(ICommandBarElement item)
48 | {
49 | // ContextMenu.SecondaryCommands.Remove(item);
50 | }
51 |
52 | private void EditActionHistory_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
53 | {
54 | CanUndo = EditActionHistory.Count > 0;
55 | InvertedEditActionHistory = new(EditActionHistory.Reverse());
56 | }
57 | private void TextAction_Copy()
58 | {
59 | if (IsSelection)
60 | {
61 | DataPackage dataPackage = new DataPackage();
62 | dataPackage.RequestedOperation = DataPackageOperation.Copy;
63 | dataPackage.SetText(SelectedText);
64 | Clipboard.SetContent(dataPackage);
65 | }
66 | }
67 |
68 | public void TextAction_Undo(EditAction action = null)
69 | {
70 | try
71 | {
72 | if (EditActionHistory.Count > 0)
73 | {
74 | if (action == null)
75 | {
76 | EditAction last = EditActionHistory.Last();
77 | Text = last.TextState;
78 | Selection = last.Selection;
79 | EditActionHistory.Remove(last);
80 | }
81 | else
82 | {
83 | int index = EditActionHistory.IndexOf(action);
84 | int end = EditActionHistory.Count - 1;
85 | for (int i = end; i >= index; i--)
86 | {
87 | EditActionHistory.RemoveAt(i);
88 | }
89 | Text = action.TextState;
90 | Selection = action.Selection;
91 | }
92 | }
93 | }
94 | catch (Exception ex)
95 | {
96 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
97 | }
98 | }
99 |
100 | public void TextAction_ToggleComment()
101 | {
102 | EditActionHistory.Add(new() { EditActionType = EditActionType.Paste, Selection = Selection, TextState = Text, TextInvolved = Language.LineComment });
103 |
104 | Place start = Selection.Start;
105 | Place end = Selection.End;
106 |
107 | for (int iline = 0; iline < SelectedLines.Count; iline++)
108 | {
109 | if (SelectedLines[iline].LineText.StartsWith(Language.LineComment))
110 | {
111 | SelectedLines[iline].SetLineText(SelectedLines[iline].LineText.Remove(0, 1));
112 | if (iline == 0)
113 | start = start - 1;
114 | if (iline == SelectedLines.Count - 1)
115 | end = end - 1;
116 | }
117 | else if (SelectedLines[iline].LineText.StartsWith(string.Concat(Enumerable.Repeat("\t", SelectedLines[iline].Indents)) + Language.LineComment))
118 | {
119 | SelectedLines[iline].SetLineText(SelectedLines[iline].LineText.Remove(SelectedLines[iline].Indents, 1));
120 | if (iline == 0)
121 | start = start - 1;
122 | if (iline == SelectedLines.Count - 1)
123 | end = end - 1;
124 | }
125 | else
126 | {
127 | SelectedLines[iline].SetLineText(SelectedLines[iline].LineText.Insert(SelectedLines[iline].Indents, Language.LineComment));
128 | if (iline == 0)
129 | start = start + 1;
130 | if (iline == SelectedLines.Count - 1)
131 | end = end + 1;
132 | }
133 | }
134 |
135 | Selection = new(start, end);
136 | CanvasText.Invalidate();
137 | updateText();
138 |
139 | CanvasScrollbarMarkers.Invalidate();
140 | LinesChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Lines)));
141 | }
142 |
143 | private void TextAction_Delete(Range selection, bool cut = false)
144 | {
145 | try
146 | {
147 | if (IsSelection)
148 | {
149 | EditActionHistory.Add(new() { EditActionType = EditActionType.Delete, Selection = selection, TextState = Text, TextInvolved = SelectedText });
150 |
151 | if (cut)
152 | {
153 | TextAction_Copy();
154 | }
155 | Place start = selection.VisualStart;
156 | Place end = selection.VisualEnd;
157 |
158 | string storetext = "";
159 | int removedlines = 0;
160 | for (int iLine = start.iLine; iLine <= end.iLine; iLine++)
161 | {
162 | if (end.iLine == start.iLine)
163 | {
164 | Lines[iLine].SetLineText(Lines[iLine].LineText.Remove(start.iChar, end.iChar - start.iChar));
165 | }
166 | else if (iLine == start.iLine)
167 | {
168 | if (start.iChar < Lines[iLine].Count)
169 | Lines[iLine].SetLineText(Lines[iLine].LineText.Remove(start.iChar));
170 | }
171 | else if (iLine == end.iLine)
172 | {
173 | if (end.iChar == Lines[iLine - removedlines].Count - 1)
174 | Lines.RemoveAt(iLine - removedlines);
175 | else
176 | {
177 | storetext = Lines[iLine - removedlines].LineText.Substring(end.iChar);
178 | Lines.RemoveAt(iLine - removedlines);
179 | }
180 | }
181 | else
182 | {
183 | Lines.RemoveAt(iLine - removedlines);
184 | removedlines += 1;
185 | }
186 | }
187 | if (!string.IsNullOrEmpty(storetext))
188 | Lines[start.iLine].AddToLineText(storetext);
189 | }
190 | }
191 | catch (Exception ex)
192 | {
193 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
194 | }
195 | updateText();
196 | Invalidate();
197 | }
198 |
199 | private void TextAction_Find()
200 | {
201 | IsFindPopupOpen = true;
202 | if (!SelectedText.Contains("\r\n"))
203 | Tbx_Search.Text = SelectedText;
204 | Tbx_Search.Focus(FocusState.Keyboard);
205 | Tbx_Search.SelectionStart = Tbx_Search.Text.Length;
206 | }
207 | private async void TextAction_Paste(string texttopaste = null, Place placetopaste = null, bool updateposition = true, DragDropModifiers dragDropModifiers = DragDropModifiers.None)
208 | {
209 | try
210 | {
211 | string text = "";
212 | if (texttopaste == null)
213 | {
214 | DataPackageView dataPackageView = Clipboard.GetContent();
215 | if (dataPackageView.Contains(StandardDataFormats.Text))
216 | {
217 | text += await dataPackageView.GetTextAsync();
218 | }
219 | }
220 | else
221 | {
222 | text = texttopaste;
223 | }
224 | EditActionHistory.Add(new() { TextState = Text, EditActionType = EditActionType.Paste, Selection = Selection, TextInvolved = text?.Replace("\t", @"\t")?.Replace("\n", @"\n") });
225 | Place place = placetopaste ?? CursorPlace;
226 | if (IsSelection && place < Selection.VisualStart && dragDropModifiers != DragDropModifiers.Control)
227 | {
228 | TextAction_Delete(Selection);
229 | Selection = new(CursorPlace);
230 | }
231 | Language lang = Language;
232 | int i = 0;
233 | text = text.Replace("\r\n", "\n");
234 | int tabcount = Lines[place.iLine].Indents;
235 | string stringtomove = "";
236 | string[] pastedlines = text.Split('\n', StringSplitOptions.None);
237 | foreach (string line in pastedlines)
238 | {
239 | if (i == 0 && text.Count(x => x == '\n') == 0)
240 | {
241 | if (place.iChar < Lines[place.iLine].LineText.Length)
242 | Lines[place.iLine].SetLineText(Lines[place.iLine].LineText.Insert(place.iChar, line));
243 | else
244 | Lines[place.iLine].SetLineText(Lines[place.iLine].LineText + line);
245 | }
246 | else if (i == 0)
247 | {
248 | stringtomove = Lines[place.iLine].LineText.Substring(place.iChar);
249 | Lines[place.iLine].SetLineText(Lines[place.iLine].LineText.Remove(place.iChar) + line);
250 | }
251 | else
252 | {
253 | var newline = new Line(lang) { LineNumber = place.iLine + 1 + i, IsUnsaved = true };
254 | newline.SetLineText(string.Concat(Enumerable.Repeat("\t", tabcount)) + line);
255 | Lines.Insert(place.iLine + i, newline);
256 | }
257 | i++;
258 | }
259 | if (!string.IsNullOrEmpty(stringtomove))
260 | Lines[place.iLine + i - 1].AddToLineText(stringtomove);
261 |
262 | if (IsSelection && place >= Selection.VisualEnd && dragDropModifiers != DragDropModifiers.Control)
263 | {
264 | TextAction_Delete(Selection);
265 | if (place.iLine == Selection.VisualEnd.iLine)
266 | Selection = new(Selection.VisualStart);
267 | }
268 |
269 | if (updateposition)
270 | {
271 | Place end = new(i == 1 ? CursorPlace.iChar + text.Length : pastedlines.Last().Length, place.iLine + i - 1);
272 | Selection = new(end);
273 | iCharPosition = CursorPlace.iChar;
274 | }
275 | }
276 | catch (Exception ex)
277 | {
278 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
279 | }
280 | updateText();
281 | Invalidate();
282 | }
283 |
284 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.DragDrop.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using System;
4 | using System.ComponentModel;
5 | using System.IO;
6 | using Windows.ApplicationModel.DataTransfer;
7 | using Windows.ApplicationModel.DataTransfer.DragDrop;
8 |
9 | namespace CodeEditorControl_WinUI;
10 |
11 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
12 | {
13 | private bool isDragging = false;
14 | private string draggedText = "";
15 | private Range draggedSelection;
16 |
17 | private async void TextControl_DragEnter(object sender, DragEventArgs e)
18 | {
19 | try
20 | {
21 | if (e.DataView.Contains(StandardDataFormats.Text) && e.AllowedOperations != DataPackageOperation.None)
22 | {
23 | Place place = await PointToPlace(e.GetPosition(TextControl));
24 | e.DragUIOverride.IsCaptionVisible = true;
25 | e.DragUIOverride.IsGlyphVisible = false;
26 | // e.AcceptedOperation = DataPackageOperation.Move | DataPackageOperation.Copy;
27 |
28 | string type;
29 | if (e.Modifiers == DragDropModifiers.Control)
30 | {
31 | e.AcceptedOperation = DataPackageOperation.Copy;
32 | type = "Paste";
33 | }
34 | else if (e.DataView.RequestedOperation == DataPackageOperation.Move)
35 | {
36 | type = "Move";
37 | e.AcceptedOperation = DataPackageOperation.Move;
38 | }
39 | else
40 | {
41 | e.AcceptedOperation = DataPackageOperation.Copy;
42 | type = "Paste";
43 | }
44 |
45 | e.DragUIOverride.Caption = $"{type}: {await e.DataView.GetTextAsync()}";
46 |
47 | e.DragUIOverride.IsContentVisible = false;
48 | IsFocused = true;
49 | tempFocus = !dragStarted;
50 |
51 | if (IsSelection && (place >= Selection.VisualStart && place < Selection.VisualEnd))
52 | {
53 | CursorPlace = Selection.End;
54 | }
55 | else
56 | {
57 | CursorPlace = place;
58 | }
59 | }
60 | else
61 | {
62 | e.AcceptedOperation = DataPackageOperation.None;
63 | }
64 | e.Handled = true;
65 | }
66 | catch (Exception ex)
67 | {
68 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
69 | }
70 | }
71 | private async void TextControl_DragOver(object sender, DragEventArgs e)
72 | {
73 | try
74 | {
75 | if (e.AcceptedOperation != DataPackageOperation.None)
76 | {
77 | Place place = await PointToPlace(e.GetPosition(TextControl));
78 | IsFocused = true;
79 | if (IsSelection && (place >= Selection.VisualStart && place < Selection.VisualEnd))
80 | {
81 | CursorPlace = Selection.End;
82 | }
83 | else
84 | {
85 | CursorPlace = place;
86 | }
87 | }
88 | e.Handled = true;
89 | }
90 | catch (Exception ex)
91 | {
92 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
93 | }
94 | }
95 |
96 | private void TextControl_DragStarting(UIElement sender, DragStartingEventArgs args)
97 | {
98 | try
99 | {
100 | args.Data.SetText(SelectedText);
101 | args.Data.RequestedOperation = DataPackageOperation.Move;
102 | args.AllowedOperations = DataPackageOperation.Move;
103 | args.DragUI.SetContentFromDataPackage();
104 | IsFocused = true;
105 | tempFocus = false;
106 | dragStarted = true;
107 | //args.DragUI.SetContentFromSoftwareBitmap(new Windows.Graphics.Imaging.SoftwareBitmap(Windows.Graphics.Imaging.BitmapPixelFormat.Rgba16, 1, 1));
108 | }
109 | catch (Exception ex)
110 | {
111 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
112 | }
113 | }
114 |
115 | private async void TextControl_Drop(object sender, DragEventArgs e)
116 | {
117 | try
118 | {
119 | if (e.DataView.Contains(StandardDataFormats.Text))
120 | {
121 | string text = await e.DataView.GetTextAsync();
122 |
123 | TextAction_Paste(text, await PointToPlace(e.GetPosition(TextControl)), true, e.Modifiers);
124 | e.Handled = true;
125 | if (tempFocus)
126 | {
127 | tempFocus = false;
128 | dragStarted = false;
129 | IsFocused = false;
130 |
131 | }
132 | else
133 | {
134 | Focus(FocusState.Pointer);
135 | }
136 | }
137 | }
138 | catch (Exception ex)
139 | {
140 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
141 | }
142 | }
143 |
144 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.Folding.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml.Controls;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace CodeEditorControl_WinUI;
9 |
10 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
11 | {
12 | List foldings = new List();
13 | private void updateFoldingPairs(Language languange)
14 | {
15 | try
16 | {
17 | foldings.Clear();
18 | if (languange.FoldingPairs != null)
19 | {
20 | foreach (var line in Lines.ToList())
21 | {
22 | if (line != null && line.Language.FoldingPairs != null)
23 | {
24 | foreach (SyntaxFolding syntaxFolding in line.Language.FoldingPairs)
25 | {
26 | var startmatch = Regex.Matches(line.LineText, syntaxFolding.RegexStart);
27 | foreach (Match match in startmatch)
28 | {
29 | if (match.Success)
30 | if (syntaxFolding.FoldingIgnoreWords == null | (syntaxFolding.FoldingIgnoreWords != null && !syntaxFolding.FoldingIgnoreWords.Contains(match.Groups[2].Value)))
31 | if (syntaxFolding.MatchingGroup > 0)
32 | foldings.Add(new() { Name = match.Groups[2].Value, StartLine = line.iLine });
33 | else
34 | foldings.Add(new() { Name = match.Value, StartLine = line.iLine });
35 | }
36 |
37 | var endmatch = Regex.Matches(line.LineText, syntaxFolding.RegexEnd);
38 | foreach (Match match in endmatch)
39 | {
40 | if (match.Success)
41 | if (syntaxFolding.FoldingIgnoreWords == null | (syntaxFolding.FoldingIgnoreWords != null && !syntaxFolding.FoldingIgnoreWords.Contains(match.Groups[2].Value)))
42 | {
43 | Folding matchingfolding;
44 | if (syntaxFolding.MatchingGroup > 0)
45 | matchingfolding = foldings.LastOrDefault(x => x.Name == match.Groups[syntaxFolding.MatchingGroup].Value);
46 | else
47 | matchingfolding = foldings.LastOrDefault(x => x.Endline == -1);
48 |
49 | if (matchingfolding != null)
50 | {
51 | matchingfolding.Endline = line.iLine;
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | foldings.RemoveAll(x => x.Endline == -1); // remove anything unmatched
59 | //InfoMessage?.Invoke(this, string.Join("", foldings.Select(x => "\n" + x.Name + ": " + x.StartLine + "->" + x.Endline)));
60 | }
61 | }
62 | catch (Exception ex)
63 | {
64 | ErrorOccured?.Invoke(this, new(ex));
65 | }
66 | }
67 |
68 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.IntelliSense.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace CodeEditorControl_WinUI;
9 |
10 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
11 | {
12 | class CommandAtPosition
13 | {
14 | public IntelliSense Command { get; set; }
15 | public Range CommandRange { get; set; }
16 | public List ArgumentsRanges { get; set; }
17 | }
18 | private List Commands
19 | {
20 | get => Get(new List() {
21 | new IntelliSense(@"\foo"){ IntelliSenseType = IntelliSenseType.Command, Token = Token.Command, Description = ""},
22 | new IntelliSense(@"\bar"){ IntelliSenseType = IntelliSenseType.Command, Token = Token.Command, Description = ""},
23 | new IntelliSense(@"\foobar"){ IntelliSenseType = IntelliSenseType.Command, Token = Token.Command, Description = ""},
24 | }); set => Set(value);
25 | }
26 | private Place SuggestionStart = new Place();
27 | private int SuggestionIndex { get => Get(-1); set { Set(value); if (value == -1) SelectedSuggestion = null; else if (Suggestions?.Count > value) { SelectedSuggestion = Suggestions[value]; Lbx_Suggestions.ScrollIntoView(SelectedSuggestion); } } }
28 | private Suggestion SelectedSuggestion { get => Get(); set => Set(value); }
29 |
30 | private List AllOptions { get => Get>(); set => Set(value); }
31 | private List AllSuggestions { get => Get(Commands); set => Set(value); }
32 | private List Suggestions { get => Get(Commands); set => Set(value); }
33 | private List Options { get => Get(new List()); set => Set(value); }
34 |
35 | public void UpdateSuggestions()
36 | {
37 | AllSuggestions = Language.Commands;
38 | Suggestions = Language.Commands;
39 | }
40 | private void InsertSuggestion()
41 | {
42 | TextControl.Focus(FocusState.Keyboard);
43 |
44 | Lines[CursorPlace.iLine].SetLineText(Lines[CursorPlace.iLine].LineText.Remove(SuggestionStart.iChar, CursorPlace.iChar - SuggestionStart.iChar));
45 |
46 | EditActionHistory.Remove(EditActionHistory.LastOrDefault());
47 | TextAction_Paste(Suggestions[SuggestionIndex].Name + Suggestions[SuggestionIndex].Snippet + Suggestions[SuggestionIndex].Options, SuggestionStart, false);
48 |
49 | int iCharStart = 0;
50 | int iCharEnd = 0;
51 | if (Suggestions[SuggestionIndex].IntelliSenseType == IntelliSenseType.Argument)
52 | {
53 | iCharStart = SuggestionStart.iChar + Suggestions[SuggestionIndex].Name.Length + Suggestions[SuggestionIndex].Snippet.Length;
54 | iCharEnd = iCharStart + Suggestions[SuggestionIndex].Options.Length;
55 | }
56 | else
57 | {
58 | iCharStart = SuggestionStart.iChar + Suggestions[SuggestionIndex].Name.Length;
59 | iCharEnd = iCharStart;
60 | }
61 | Selection = new(new Place(iCharStart, CursorPlace.iLine), new Place(iCharEnd, CursorPlace.iLine));
62 | IsSuggesting = false;
63 | }
64 |
65 | private void FilterSuggestions(int offset = 0)
66 | {
67 | if (IsSuggesting)
68 | {
69 | try
70 | {
71 | string searchString = Lines[SuggestionStart.iLine].LineText.Substring(SuggestionStart.iChar, CursorPlace.iChar - SuggestionStart.iChar);
72 | List matchingSuggestions;
73 | if (!IsSuggestingOptions)
74 | {
75 | matchingSuggestions = AllSuggestions
76 | .Where(m => m.Name.Contains(searchString))
77 | .OrderBy(m => m.Name)
78 | .ToList();
79 | }
80 | else
81 | {
82 | matchingSuggestions = AllOptions
83 | .Where(m => m.Name.Contains(searchString))
84 | .OrderBy(m => m.Name)
85 | .ToList();
86 | }
87 | if (matchingSuggestions.Count > 0)
88 | {
89 | Suggestions = matchingSuggestions;
90 | SuggestionIndex = 0;
91 | }
92 | else
93 | SuggestionIndex = -1;
94 | }
95 | catch
96 | {
97 |
98 | }
99 | }
100 | }
101 |
102 | private CommandAtPosition GetCommandAtPosition(Place place)
103 | {
104 | CommandAtPosition commandAtPosition = new CommandAtPosition();
105 | MatchCollection commandsInLine = Regex.Matches(Lines[place.iLine].LineText, @"(\\.+?\b)(\[(?>\[(?)|[^\[\]]+|\](?<-c>))*(?(c)(?!))\])*");
106 | if (commandsInLine.Any() && AllSuggestions != null)
107 | {
108 | foreach (Match command in commandsInLine)
109 | {
110 | if (command.Success && place.iChar >= command.Index && place.iChar <= command.Index + command.Length)
111 | {
112 | var commandname = command.Groups[1];
113 | commandAtPosition.CommandRange = new(new(commandname.Index, place.iLine), new(command.Index + command.Length, place.iLine));
114 | commandAtPosition.Command = AllSuggestions.FirstOrDefault(x => x.Name == commandname.Value) as IntelliSense;
115 | if (command.Groups.Count > 2)
116 | {
117 | commandAtPosition.ArgumentsRanges = new();
118 | for (int group = 2; group < command.Groups.Count; group++)
119 | {
120 | var argument = command.Groups[group];
121 | commandAtPosition.ArgumentsRanges.Add(new(new(argument.Index, place.iLine), new(argument.Index + argument.Length, place.iLine)));
122 | }
123 | }
124 | return commandAtPosition;
125 | }
126 | }
127 | }
128 | return commandAtPosition;
129 | }
130 |
131 | private IntelliSense GetCommandFromPlace(Place place)
132 | {
133 | Place start = place;
134 | Place end = new Place(start.iChar + 1, start.iLine);
135 | var matches = Regex.Matches(Lines[start.iLine].LineText, @"(\\.+?)(\s*?)(\[)");
136 | string command = "";
137 | foreach (Match match in matches)
138 | {
139 | command = match?.Groups[1]?.Value;
140 | }
141 | if (!string.IsNullOrEmpty(command))
142 | return AllSuggestions.FirstOrDefault(x => x.Name == command) as IntelliSense;
143 | else return null;
144 | }
145 |
146 | private bool IsInsideBrackets(Place place)
147 | {
148 | List pairs = new();
149 |
150 | for (int i = 0; i < Lines[place.iLine].LineText.Length; i++)
151 | {
152 | if (Lines[place.iLine].LineText[i] == '[')
153 | {
154 | int open = i;
155 | int close = findClosingParen(Lines[place.iLine].LineText.ToCharArray(), i);
156 | pairs.Add(new BracketPair(new Place(open, place.iLine), new Place(close, place.iLine)));
157 | }
158 | }
159 |
160 | return pairs.Any(x => x.iClose >= place && x.iOpen < place);
161 | }
162 |
163 | private int findClosingParen(char[] text, int openPos)
164 | {
165 | int closePos = openPos;
166 | int counter = 1;
167 | while (counter > 0)
168 | {
169 | if (closePos == text.Length - 1)
170 | {
171 | return ++closePos;
172 | }
173 | char c = text[++closePos];
174 | if (c == '[')
175 | {
176 | counter++;
177 | }
178 | else if (c == ']')
179 | {
180 | counter--;
181 | }
182 | }
183 | return closePos;
184 | }
185 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.Properties.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI;
2 | using Microsoft.UI.Xaml;
3 | using Microsoft.UI.Xaml.Controls;
4 | using System;
5 | using System.Collections.ObjectModel;
6 | using System.ComponentModel;
7 | using System.IO;
8 | using System.Threading.Tasks;
9 | using System.Windows.Input;
10 | using Windows.System;
11 | using Windows.UI;
12 |
13 | namespace CodeEditorControl_WinUI;
14 |
15 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
16 | {
17 | /// Dependency Properties
18 | private static new readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
19 | "FontSize", typeof(int), typeof(CodeWriter), new PropertyMetadata(12, (d, e) => ((CodeWriter)d).FontSizeChanged(d, e)));
20 |
21 | public static readonly DependencyProperty FontProperty = DependencyProperty.Register(
22 | "Font", typeof(string), typeof(CodeWriter), new PropertyMetadata("Consolas", (d, e) => ((CodeWriter)d).FontChanged(d, e)));
23 |
24 | public static readonly DependencyProperty IsFoldingEnabledProperty = DependencyProperty.Register(
25 | "IsFoldingEnabled", typeof(bool), typeof(CodeWriter), new PropertyMetadata(true, (d, e) => ((CodeWriter)d).Invalidate()));
26 |
27 | public static readonly DependencyProperty IsWrappingEnabledProperty = DependencyProperty.Register(
28 | "IsWrappingEnabled", typeof(bool), typeof(CodeWriter), new PropertyMetadata(true, (d, e) => ((CodeWriter)d).Invalidate()));
29 |
30 | public static readonly DependencyProperty WrappingLengthProperty = DependencyProperty.Register(
31 | "WrappingLength", typeof(int), typeof(CodeWriter), new PropertyMetadata(1, (d, e) => ((CodeWriter)d).Invalidate()));
32 |
33 | public static new readonly DependencyProperty LanguageProperty = DependencyProperty.Register(
34 | "Language", typeof(Language), typeof(CodeWriter), new PropertyMetadata(new Language("ConTeXt"), (d, e) => ((CodeWriter)d).LanguageChanged()));
35 |
36 | public static new readonly DependencyProperty RequestedThemeProperty = DependencyProperty.Register(
37 | "RequestedTheme", typeof(ElementTheme), typeof(CodeWriter), new PropertyMetadata(12, (d, e) => ((CodeWriter)d).RequestedThemeChanged(d, e)));
38 |
39 | public static readonly DependencyProperty ScrollPositionProperty = DependencyProperty.Register(
40 | "ScrollPosition", typeof(Place), typeof(CodeWriter), new PropertyMetadata(new Place(), (d, e) => ((CodeWriter)d).OnScrollPositionChanged(d, e)));
41 |
42 | public static readonly DependencyProperty ShowControlCharactersProperty = DependencyProperty.Register(
43 | "ShowControlCharacters", typeof(bool), typeof(CodeWriter), new PropertyMetadata(true, (d, e) => ((CodeWriter)d).OnShowControlCharactersChanged(d, e)));
44 |
45 | public static readonly DependencyProperty ShowIndentGuidesProperty = DependencyProperty.Register(
46 | "ShowIndentGuides", typeof(IndentGuide), typeof(CodeWriter), new PropertyMetadata(IndentGuide.None, (d, e) => ((CodeWriter)d).Invalidate()));
47 |
48 | public static readonly DependencyProperty ShowHorizontalTicksProperty = DependencyProperty.Register(
49 | "ShowHorizontalTicks", typeof(bool), typeof(CodeWriter), new PropertyMetadata(false, (d, e) => ((CodeWriter)d).Invalidate()));
50 |
51 | public static readonly DependencyProperty ShowLineMarkersProperty = DependencyProperty.Register(
52 | "ShowLineMarkers", typeof(bool), typeof(CodeWriter), new PropertyMetadata(true, (d, e) => ((CodeWriter)d).Invalidate()));
53 |
54 | public static readonly DependencyProperty ShowLineNumbersProperty = DependencyProperty.Register(
55 | "ShowLineNumbers", typeof(bool), typeof(CodeWriter), new PropertyMetadata(true, (d, e) => ((CodeWriter)d).Invalidate()));
56 |
57 | public static readonly DependencyProperty ShowScrollbarMarkersProperty = DependencyProperty.Register(
58 | "ShowScrollbarMarkers", typeof(bool), typeof(CodeWriter), new PropertyMetadata(true, (d, e) => ((CodeWriter)d).Invalidate()));
59 |
60 | public static readonly DependencyProperty ShowScrollbarsProperty = DependencyProperty.Register(
61 | "ShowScrollbars", typeof(bool), typeof(CodeWriter), new PropertyMetadata(false, (d, e) => { ((CodeWriter)d).OnShowScrollbarsChanged(d, e); }));
62 |
63 | public static readonly DependencyProperty TabLengthProperty = DependencyProperty.Register(
64 | "TabLength", typeof(int), typeof(CodeWriter), new PropertyMetadata(2, (d, e) => ((CodeWriter)d).OnTabLengthChanged(d, e)));
65 |
66 | public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
67 | "Text", typeof(string), typeof(CodeWriter), new PropertyMetadata(null, (d, e) => ((CodeWriter)d).OnTextChanged(d, e)));
68 |
69 | public static readonly DependencyProperty CurrentLineProperty = DependencyProperty.Register(
70 | "CurrentLine", typeof(Place), typeof(CodeWriter), new PropertyMetadata(new Place(0, 0), (d, e) => ((CodeWriter)d).CurrentLineChanged(d, e)));
71 |
72 | public static readonly DependencyProperty EditActionHistoryProperty = DependencyProperty.Register(
73 | "EditActionHistory", typeof(ObservableCollection), typeof(CodeWriter), new PropertyMetadata(new ObservableCollection(), (d, e) => ((CodeWriter)d).EditActionHistoryChanged(d, e)));
74 |
75 | public bool ShowScrollbars { get => (bool)GetValue(ShowScrollbarsProperty); set => SetValue(ShowScrollbarsProperty, value); }
76 |
77 | private void FontChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
78 | {
79 | switch (Font)
80 | {
81 | case "Fira Code": FontUri = "CodeEditorControl-WinUI/Fonts/FiraCode.ttf#Fira Code"; break;
82 | case "JetBrains Mono": FontUri = "CodeEditorControl-WinUI/Fonts/JetBrainsMono.ttf#JetBrains Mono Thin"; break;
83 | default: FontUri = Font; break;
84 | }
85 |
86 | int currline = 0;
87 |
88 | if (VerticalScroll != null)
89 | {
90 | currline = (int)VerticalScroll.Value / CharHeight;
91 | }
92 | Invalidate(true);
93 | if (VerticalScroll != null)
94 | {
95 | VerticalScroll.Value = currline * CharHeight;
96 | }
97 |
98 | IntelliSenseWidth = Math.Max(150, Math.Min(20 * CharWidth, 300));
99 | }
100 |
101 | private void FontSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
102 | {
103 | int currline = 0;
104 |
105 | if (VerticalScroll != null)
106 | {
107 | currline = (int)VerticalScroll.Value / CharHeight;
108 | }
109 | Invalidate(true);
110 | if (VerticalScroll != null)
111 | {
112 | VerticalScroll.Value = currline * CharHeight;
113 | }
114 |
115 | IntelliSenseWidth = Math.Max(150, Math.Min(20 * CharWidth, 300));
116 | }
117 | private async void LanguageChanged()
118 | {
119 | CanToggleComment = !string.IsNullOrEmpty(Language.LineComment);
120 | //Language lang = Language ?? Languages.ConTeXt;
121 | //await Task.Run(
122 | // () =>
123 | //{
124 | //bool innerLang = false;
125 |
126 | //foreach (Line line in Lines)
127 | //{
128 | // if (innerLang)
129 | // {
130 | // line.Language = Languages.LanguageList.FirstOrDefault(x=>x.Name == );
131 | // }
132 | // else
133 | // {
134 | // line.Language = Language;
135 | // }
136 |
137 | // if (line.LineText.Contains("\\startlua") | line.LineText.Contains("\\startluacode"))
138 | // {
139 | // innerLang = true;
140 | // }
141 | //}
142 |
143 | foreach (Line line in Lines)
144 | {
145 | line.Language = Language;
146 | }
147 | DispatcherQueue.TryEnqueue(() => { Invalidate(); });
148 | //});
149 | }
150 | private void OnScrollPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
151 | {
152 | }
153 | private void OnShowControlCharactersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
154 | {
155 | CanvasText.Invalidate();
156 | }
157 | private void OnTabLengthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
158 | {
159 | Invalidate();
160 | }
161 | private async void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
162 | {
163 | if (!(d as CodeWriter).IsSettingValue)
164 | {
165 | while (!CanvasText.IsLoaded | !CanvasBeam.IsLoaded | !CanvasSelection.IsLoaded | !CanvasScrollbarMarkers.IsLoaded) // ToDo: Very ugly workaround, logic needs to be overthought
166 | await Task.Delay(10);
167 | await InitializeLines((string)e.NewValue);
168 |
169 | TextChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
170 |
171 |
172 |
173 | TextChangedTimer.Stop();
174 | TextChangedTimer = new() { Interval = TimeSpan.FromMilliseconds(200) };
175 | TextChangedTimerLastText = Text;
176 | TextChangedTimer.Tick += (a, b) =>
177 | {
178 | if (Text != TextChangedTimerLastText)
179 | {
180 | textChanged();
181 |
182 | }
183 |
184 | };
185 | TextChangedTimer.Start();
186 | }
187 | }
188 | private void RequestedThemeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
189 | {
190 | try
191 | {
192 | ActualTheme = (ElementTheme)e.NewValue;
193 |
194 | if ((ElementTheme)e.NewValue == ElementTheme.Default)
195 | {
196 | var backcolor = new Windows.UI.ViewManagement.UISettings().GetColorValue(Windows.UI.ViewManagement.UIColorType.Background);
197 | ActualTheme = backcolor.Equals(Colors.White) ? ElementTheme.Light : ElementTheme.Dark;
198 | }
199 |
200 | if (ActualTheme == ElementTheme.Light)
201 | {
202 | //Background = new SolidColorBrush(Color.FromArgb(150, 252, 252, 252));
203 | //Color_LeftBackground = Color.FromArgb(255, 230, 230, 230);
204 | //Color_LineNumber = Color.FromArgb(255, 120, 160, 180).InvertColorBrightness;
205 | }
206 | else
207 | {
208 | //Background = new SolidColorBrush(Color.FromArgb(150, 28, 28, 28));
209 | //Color_LeftBackground = Color.FromArgb(255, 25, 25, 25);
210 | //Color_LineNumber = Color.FromArgb(255, 120, 160, 180);
211 | }
212 | }
213 | catch (Exception ex)
214 | {
215 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
216 | }
217 | Invalidate();
218 | }
219 |
220 | public bool IsFoldingEnabled { get => (bool)GetValue(IsFoldingEnabledProperty); set { SetValue(IsFoldingEnabledProperty, value); } }
221 | public bool IsWrappingEnabled { get => (bool)GetValue(IsWrappingEnabledProperty); set { SetValue(IsWrappingEnabledProperty, value); } }
222 | public Place CurrentLine { get => (Place)GetValue(CurrentLineProperty); set { SetValue(CurrentLineProperty, value); } }
223 | public int TabLength { get => (int)GetValue(TabLengthProperty); set => SetValue(TabLengthProperty, value); }
224 | public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
225 | public bool ShowControlCharacters { get => (bool)GetValue(ShowControlCharactersProperty); set => SetValue(ShowControlCharactersProperty, value); }
226 | public IndentGuide ShowIndentGuides { get => (IndentGuide)GetValue(ShowIndentGuidesProperty); set => SetValue(ShowIndentGuidesProperty, value); }
227 | public bool ShowHorizontalTicks { get => (bool)GetValue(ShowHorizontalTicksProperty); set { SetValue(ShowHorizontalTicksProperty, value); } }
228 | public bool ShowLineMarkers { get => (bool)GetValue(ShowLineMarkersProperty); set { SetValue(ShowLineMarkersProperty, value); } }
229 | public bool ShowLineNumbers { get => (bool)GetValue(ShowLineNumbersProperty); set { SetValue(ShowLineNumbersProperty, value); } }
230 | public bool ShowScrollbarMarkers { get => (bool)GetValue(ShowScrollbarMarkersProperty); set { SetValue(ShowScrollbarMarkersProperty, value); } }
231 | public new ElementTheme RequestedTheme { get => (ElementTheme)GetValue(RequestedThemeProperty); set => SetValue(RequestedThemeProperty, value); }
232 | public Place ScrollPosition { get => (Place)GetValue(ScrollPositionProperty); set => SetValue(ScrollPositionProperty, value); }
233 | public new Language Language { get => (Language)GetValue(LanguageProperty); set { SetValue(LanguageProperty, value); } }
234 | public int WrappingLength { get => (int)GetValue(WrappingLengthProperty); set { SetValue(WrappingLengthProperty, value); } }
235 |
236 |
237 | public int CharHeight { get => Get(16); set { Set(value); } }
238 | public int CharWidth { get => Get(8); set { Set(value); } }
239 |
240 | /// Commands
241 | public ICommand Command_Copy { get; set; }
242 | public ICommand Command_Cut { get; set; }
243 | public ICommand Command_Delete { get; set; }
244 | public ICommand Command_Find { get; set; }
245 | public ICommand Command_Paste { get; set; }
246 | public ICommand Command_SelectAll { get; set; }
247 | public ICommand Command_Undo { get; set; }
248 | public ICommand Command_ToggleComment { get; set; }
249 |
250 | /// Colors
251 | public Color Color_Beam { get => Get(Color.FromArgb(255, 200, 200, 200)); set => Set(value); }
252 | public Color Color_FoldingMarker { get => Get(Color.FromArgb(255, 140, 140, 140)); set => Set(value); }
253 | public Color Color_FoldingMarkerUnselected { get => Get(Color.FromArgb(150, 140, 140, 140)); set => Set(value); }
254 | public Color Color_Background { get => Get(Color.FromArgb(25, 135, 135, 135)); set => Set(value); }
255 | public Color Color_LeftBackground { get => Get(Color.FromArgb(10, 135, 135, 135)); set => Set(value); }
256 | public Color Color_LineNumber { get => Get(Color.FromArgb(255, 210, 210, 210)); set => Set(value); }
257 | public Color Color_LineNumberUnselected { get => Get(Color.FromArgb(160, 210, 210, 210)); set => Set(value); }
258 | public Color Color_SelelectedLineBackground { get => Get(Color.FromArgb(20, 210, 210, 210)); set => Set(value); }
259 | public Color Color_Selection { get => Get(Color.FromArgb(255, 50, 75, 100)); set => Set(value); }
260 | public Color Color_UnsavedMarker { get => Get(Color.FromArgb(255, 80, 190, 230)); set => Set(value); }
261 | public Color Color_WeakMarker { get => Get(Color.FromArgb(255, 60, 60, 60)); set => Set(value); }
262 |
263 | /// Font
264 | public new int FontSize { get => Math.Max((int)GetValue(FontSizeProperty),1); set { SetValue(FontSizeProperty, value); } }
265 | public string Font { get => (string)GetValue(FontProperty); set { SetValue(FontProperty, value); } }
266 | private string FontUri { get; set; } = "Consolas";
267 | private new float Scale { get { return XamlRoot != null && XamlRoot?.RasterizationScale != null ? (float)XamlRoot.RasterizationScale : 1.0f; } }
268 | public int ScaledFontSize { get => (int)((float)FontSize * Scale); }
269 | public int startFontsize = 16;
270 | public int MaxFontSize = 100;
271 | public int MinFontSize = 6;
272 |
273 | /// Geometry
274 | private int IntelliSenseWidth { get => Get(300); set => Set(value); }
275 | public int HorizontalOffset { get => Get(0); set { Set(value); } }
276 | private int Width_ErrorMarker { get; set; } = 12;
277 | private int Width_FoldingMarker { get; set; } = 24;
278 | private int Width_TextIndent { get => CharWidth / 2; }
279 | private int Width_WarningMarker { get; set; } = 12;
280 | public int Width_Left { get => (int)Width_LeftMargin + (int)Width_LineNumber + (int)Width_ErrorMarker + (int)Width_WarningMarker + (int)Width_FoldingMarker + Width_TextIndent; }
281 | public int Width_LineNumber { get => Get(12); set => Set(value); }
282 | public int Width_LeftMargin { get => Get(6); set => Set(value); }
283 | public bool WordWrap { get; private set; } = true;
284 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.Scrolling.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Input;
2 | using Microsoft.UI.Xaml;
3 | using Microsoft.UI.Xaml.Controls;
4 | using Microsoft.UI.Xaml.Controls.Primitives;
5 | using Microsoft.UI.Xaml.Input;
6 | using System;
7 | using System.ComponentModel;
8 | using System.IO;
9 | using System.Timers;
10 | using Windows.Foundation;
11 | using Windows.System;
12 |
13 | namespace CodeEditorControl_WinUI;
14 |
15 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
16 | {
17 | private Point middleClickScrollingStartPoint = new Point();
18 | private Point middleClickScrollingEndPoint = new Point();
19 |
20 | public void ScrollToLine(int iLine)
21 | {
22 | VerticalScroll.Value = (iLine + 1) * CharHeight - TextControl.ActualHeight / 2;
23 |
24 | }
25 |
26 | public void CenterView()
27 | {
28 | HorizontalScroll.Value = 0;
29 | VerticalScroll.Value = (Selection.VisualStart.iLine + 1) * CharHeight - TextControl.ActualHeight / 2;
30 | Focus(FocusState.Keyboard);
31 | }
32 |
33 | private void Scroll_SizeChanged(object sender, SizeChangedEventArgs e)
34 | {
35 | if (isCanvasLoaded)
36 | Invalidate();
37 | }
38 |
39 | private void ScrollContent_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
40 | {
41 | if (e.PointerDeviceType == Microsoft.UI.Input.PointerDeviceType.Touch)
42 | {
43 | int scalesign = Math.Sign(e.Delta.Scale - 1);
44 |
45 | FontSize = Math.Min(Math.Max((int)(startFontsize * e.Cumulative.Scale), MinFontSize), MaxFontSize);
46 | HorizontalScroll.Value -= e.Delta.Translation.X;
47 | VerticalScroll.Value -= e.Delta.Translation.Y;
48 | }
49 | }
50 |
51 | private void ScrollContent_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e)
52 | {
53 | startFontsize = FontSize;
54 | }
55 |
56 | private void Scroll_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
57 | {
58 | try
59 | {
60 | PointerPoint pointer = e.GetCurrentPoint(Scroll);
61 | int mwd = pointer.Properties.MouseWheelDelta;
62 |
63 | if (e.KeyModifiers == VirtualKeyModifiers.Control)
64 | {
65 | int newfontsize = FontSize + Math.Sign(mwd);
66 | if (newfontsize >= MinFontSize && newfontsize <= MaxFontSize)
67 | SetValue(FontSizeProperty, newfontsize);
68 | }
69 | else
70 | {
71 | if (pointer.Properties.IsHorizontalMouseWheel)
72 | {
73 | if (mwd % 120 == 0) // Mouse
74 | {
75 | HorizontalScroll.Value += 6 * mwd / 120 * CharWidth;
76 | }
77 | else // Trackpad
78 | {
79 | HorizontalScroll.Value += mwd;
80 | }
81 | }
82 | else if (e.KeyModifiers == VirtualKeyModifiers.Shift)
83 | {
84 | if (mwd % 120 == 0) // Mouse
85 | {
86 | HorizontalScroll.Value -= 3 * mwd / 120 * CharWidth;
87 | }
88 | else // Trackpad
89 | {
90 | HorizontalScroll.Value -= mwd;
91 | }
92 | }
93 | else
94 | {
95 | if (mwd % 120 == 0) // Mouse
96 | {
97 | VerticalScroll.Value -= 3 * mwd / 120 * CharHeight;
98 | }
99 | else // Trackpad
100 | {
101 | VerticalScroll.Value -= mwd;
102 | }
103 | }
104 | }
105 | IsSuggesting = false;
106 | e.Handled = true;
107 | }
108 | catch (Exception ex)
109 | {
110 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
111 | }
112 | }
113 | private void VerticalScroll_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
114 | {
115 | try
116 | {
117 |
118 | if (e.NewValue == e.OldValue | VisibleLines == null | VisibleLines.Count == 0)
119 | {
120 | return;
121 | }
122 | int updown = e.NewValue > e.OldValue ? -1 : 0;
123 | if (Math.Abs((int)e.NewValue - (VisibleLines[0].LineNumber + updown) * CharHeight) < CharHeight)
124 | {
125 | return;
126 | }
127 | Invalidate();
128 | }
129 | catch (Exception ex)
130 | {
131 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
132 | }
133 | }
134 | private void HorizontalScroll_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
135 | {
136 | if (e.NewValue == e.OldValue)
137 | {
138 | return;
139 | }
140 | int n = Math.Max((int)(e.NewValue / CharWidth) * CharWidth, 0);
141 | iCharOffset = (int)(n / CharWidth);
142 | HorizontalOffset = -n;
143 | CanvasBeam.Invalidate();
144 | CanvasSelection.Invalidate();
145 | CanvasText.Invalidate();
146 | }
147 | private void VerticalScroll_Scroll(object sender, ScrollEventArgs e)
148 | {
149 | }
150 |
151 | private void TextControl_PointerExited(object sender, PointerRoutedEventArgs e)
152 | {
153 | try
154 | {
155 | /* // Proplem: e.GetCurrentPoint() doesn't give the current Point anymore, bug in WinAppSDK 1.1.X; ToDo: Replace zombie code with workaround
156 | PointerPoint point = e.GetCurrentPoint(TextControl);
157 | if (point.Properties.IsLeftButtonPressed && isSelecting)
158 | {
159 | if (point.Position.Y < 0)
160 | {
161 | DispatcherTimer Timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
162 | Timer.Tick += async (a, b) =>
163 | {
164 | PointerPoint pointup = e.GetCurrentPoint(TextControl);
165 | InfoMessage?.Invoke(this, $"ScrollUp at point: {CurrentPointer.Position} with LeftButtonPressed: {InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.LeftButton)}");
166 |
167 | if (pointup.Position.Y > 0 | InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.LeftButton) == (Windows.UI.Core.CoreVirtualKeyStates.None | Windows.UI.Core.CoreVirtualKeyStates.Locked))
168 | {
169 | ((DispatcherTimer)a).Stop();
170 | InfoMessage?.Invoke(this, "ScrollUp Stopped") ;
171 | }
172 | VerticalScroll.Value -= CharHeight;
173 | Selection = new(Selection.Start, await PointToPlace(pointup.Position));
174 | };
175 | Timer.Start();
176 | }
177 | else if (point.Position.Y > Scroll.ActualHeight - 2 * CharHeight)
178 | {
179 | DispatcherTimer Timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
180 | Timer.Tick += async (a, b) =>
181 | {
182 | PointerPoint pointdown = e.GetCurrentPoint(TextControl);
183 | if (pointdown.Position.Y < Scroll.ActualHeight | !pointdown.Properties.IsLeftButtonPressed)
184 | {
185 | ((DispatcherTimer)a).Stop();
186 | }
187 | VerticalScroll.Value += pointdown.Position.Y - Scroll.ActualHeight;
188 | Selection = new(Selection.Start, await PointToPlace(pointdown.Position));
189 | };
190 | Timer.Start();
191 | }
192 |
193 | if (point.Position.X < 0)
194 | {
195 | DispatcherTimer Timer = new() { Interval = TimeSpan.FromMilliseconds(100) };
196 | Timer.Tick += async (a, b) =>
197 | {
198 | PointerPoint pointleft = e.GetCurrentPoint(TextControl);
199 | if (pointleft.Position.X > 0 | !pointleft.Properties.IsLeftButtonPressed)
200 | {
201 | ((Timer)a).Stop();
202 | }
203 | HorizontalScroll.Value += pointleft.Position.X;
204 | Selection = new(Selection.Start, await PointToPlace(pointleft.Position));
205 | };
206 | Timer.Start();
207 | }
208 | else if (point.Position.X >= Scroll.ActualWidth - 2 * CharWidth)
209 | {
210 | DispatcherTimer Timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
211 | Timer.Tick += async (a, b) =>
212 | {
213 | PointerPoint pointright = e.GetCurrentPoint(TextControl);
214 | if (pointright.Position.X < Scroll.ActualWidth | !pointright.Properties.IsLeftButtonPressed)
215 | {
216 | ((DispatcherTimer)a).Stop();
217 | }
218 | HorizontalScroll.Value += pointright.Position.X - Scroll.ActualWidth;
219 | Selection = new(Selection.Start, await PointToPlace(pointright.Position));
220 | };
221 | Timer.Start();
222 | }
223 | }
224 | e.Handled = true;
225 | TextControl.Focus(FocusState.Pointer);
226 | */
227 | }
228 | catch (Exception ex)
229 | {
230 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
231 | }
232 | }
233 |
234 | private void TextControl_PointerLost(object sender, PointerRoutedEventArgs e)
235 | {
236 | if (isSelecting)
237 | {
238 | e.Handled = true;
239 | TextControl.Focus(FocusState.Pointer);
240 | }
241 | isSelecting = false;
242 | isLineSelect = false;
243 | isDragging = false;
244 | tempFocus = false;
245 | }
246 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.Search.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Input;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text.RegularExpressions;
10 | using Windows.System;
11 |
12 | namespace CodeEditorControl_WinUI;
13 |
14 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
15 | {
16 | private int searchindex = 0;
17 | public bool IsFindPopupOpen { get => Get(false); set { Set(value); if (!value) { Tbx_Search.Text = ""; TextControl.Focus(FocusState.Keyboard); } } }
18 | public bool IsMatchCase { get => Get(false); set { Set(value); Tbx_SearchChanged(null, null); } }
19 | public bool IsRegex { get => Get(false); set { Set(value); Tbx_SearchChanged(null, null); } }
20 |
21 | public List SearchMatches { get => Get(new List()); set { Set(value); CanvasScrollbarMarkers.Invalidate(); } }
22 |
23 | private void Btn_SearchClose(object sender, RoutedEventArgs e)
24 | {
25 | IsFindPopupOpen = false;
26 | }
27 |
28 | private void Btn_SearchNext(object sender, RoutedEventArgs e)
29 | {
30 | try
31 | {
32 | searchindex++;
33 | if (searchindex >= SearchMatches.Count)
34 | {
35 | searchindex = 0;
36 | }
37 |
38 | if (SearchMatches.Count > 0)
39 | {
40 | SearchMatch sm = SearchMatches[searchindex];
41 | Selection = new(new Place(sm.iChar, sm.iLine));
42 | }
43 | }
44 | catch (Exception ex)
45 | {
46 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
47 | }
48 | }
49 | private void Tbx_Search_KeyDown(object sender, KeyRoutedEventArgs e)
50 | {
51 | switch (e.Key)
52 | {
53 | case VirtualKey.Enter:
54 | Btn_SearchNext(null, null);
55 | e.Handled = true;
56 | break;
57 | case VirtualKey.Escape:
58 | IsFindPopupOpen = false;
59 | e.Handled = true;
60 | break;
61 | case VirtualKey.Tab:
62 | Tbx_Replace.Focus(FocusState.Keyboard);
63 | e.Handled = true;
64 | break;
65 | case VirtualKey.Back:
66 | e.Handled = true;
67 | break;
68 | }
69 | }
70 | private async void Btn_ReplaceNext(object sender, RoutedEventArgs e)
71 | {
72 | try
73 | {
74 | if (SearchMatches.Count > 0)
75 | {
76 | SearchMatch sm = SearchMatches[searchindex];
77 | Lines[sm.iLine].SetLineText(Lines[sm.iLine].LineText.Remove(sm.iChar, sm.Match.Length).Insert(sm.iChar, Tbx_Replace.Text));
78 | EditActionHistory.Add(new() { EditActionType = EditActionType.Paste, Selection = Selection, TextInvolved = Tbx_Replace.Text, TextState = Text });
79 | SearchMatches.RemoveAt(searchindex);
80 | updateText();
81 | Invalidate();
82 | Selection = new(new(sm.iChar, sm.iLine), new(sm.iChar + Tbx_Replace.Text.Length, sm.iLine));
83 |
84 | }
85 | }
86 | catch (Exception ex)
87 | {
88 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
89 | }
90 | }
91 |
92 | private async void Btn_ReplaceAll(object sender, RoutedEventArgs e)
93 | {
94 | try
95 | {
96 | if (SearchMatches.Count > 0)
97 | {
98 | foreach (SearchMatch sm in SearchMatches)
99 | {
100 | Lines[sm.iLine].SetLineText(Lines[sm.iLine].LineText.Remove(sm.iChar, sm.Match.Length).Insert(sm.iChar, Tbx_Replace.Text));
101 | }
102 | EditActionHistory.Add(new() { EditActionType = EditActionType.Paste, Selection = Selection, TextInvolved = "< multiple replacements >", TextState = Text });
103 | Selection = new(new(SearchMatches.Last().iChar, SearchMatches.Last().iLine), new(SearchMatches.Last().iChar + Tbx_Replace.Text.Length, SearchMatches.Last().iLine));
104 | SearchMatches.Clear();
105 | updateText();
106 | Invalidate();
107 | }
108 | }
109 | catch (Exception ex)
110 | {
111 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
112 | }
113 | }
114 | private void Tbx_Replace_KeyDown(object sender, KeyRoutedEventArgs e)
115 | {
116 | switch (e.Key)
117 | {
118 | case VirtualKey.Enter:
119 | Btn_ReplaceNext(null, null);
120 | e.Handled = true;
121 | break;
122 | case VirtualKey.Escape:
123 | IsFindPopupOpen = false;
124 | e.Handled = true;
125 | break;
126 | case VirtualKey.Tab:
127 | Tbx_Search.Focus(FocusState.Keyboard);
128 | e.Handled = true;
129 | break;
130 | case VirtualKey.Back:
131 | e.Handled = true;
132 | break;
133 | }
134 | }
135 |
136 | Microsoft.UI.Dispatching.DispatcherQueue _queue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
137 | private void Tbx_SearchChanged(object sender, TextChangedEventArgs e)
138 | {
139 | try
140 | {
141 | searchindex = 0;
142 | string text = Tbx_Search.Text;
143 |
144 |
145 | _queue.TryEnqueue(() =>
146 | {
147 | if (text == "")
148 | {
149 | SearchMatches.Clear();
150 | CanvasScrollbarMarkers.Invalidate();
151 | CanvasSelection.Invalidate();
152 | return;
153 | }
154 |
155 | SearchMatches.Clear();
156 | for (int iLine = 0; iLine < Lines.Count; iLine++)
157 | {
158 | if (IsRegex)
159 | {
160 | bool isValidRegex = true;
161 | MatchCollection coll = null;
162 | try
163 | {
164 | coll = Regex.Matches(Lines[iLine].LineText, text, IsMatchCase ? RegexOptions.None : RegexOptions.IgnoreCase);
165 | }
166 | catch
167 | {
168 | isValidRegex = false;
169 | }
170 | if (isValidRegex && coll != null)
171 | {
172 | foreach (Match m in coll)
173 | SearchMatches.Add(new() { Match = m.Value, iChar = m.Index, iLine = iLine });
174 | }
175 | }
176 | else
177 | {
178 | int nextindex = 0;
179 |
180 | while (nextindex != -1)
181 | {
182 | nextindex = Lines[iLine].LineText.IndexOf(text, nextindex, IsMatchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase);
183 | if (nextindex != -1)
184 | {
185 | SearchMatches.Add(new() { Match = text, iChar = nextindex, iLine = iLine });
186 | nextindex++;
187 | }
188 | }
189 | }
190 | }
191 | if (SearchMatches.Count > 0)
192 | {
193 | Selection = new Range(new Place(SearchMatches[0].iChar, SearchMatches[0].iLine));
194 | }
195 | CanvasScrollbarMarkers.Invalidate();
196 | CanvasSelection.Invalidate();
197 | });
198 | }
199 | catch (Exception ex)
200 | {
201 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
202 | }
203 | }
204 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.Selection.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Input;
2 | using Microsoft.UI.Xaml;
3 | using Microsoft.UI.Xaml.Controls;
4 | using Microsoft.UI.Xaml.Input;
5 | using System;
6 | using System.ComponentModel;
7 | using System.IO;
8 | using System.Linq;
9 | using System.Text.RegularExpressions;
10 | using Windows.Foundation;
11 |
12 | namespace CodeEditorControl_WinUI;
13 |
14 | public partial class CodeWriter : UserControl, INotifyPropertyChanged
15 | {
16 | private Point previousPosition = new Point() { };
17 |
18 | private PointerPoint CurrentPointer { get; set; }
19 | public void SelectLine(Place start)
20 | {
21 | Place end = new(Lines[start.iLine].Count, start.iLine);
22 | Selection = new(start, end);
23 | }
24 |
25 | public void TextAction_SelectText(Range range = null)
26 | {
27 | if (range == null && Lines.Count > 0)
28 | {
29 | Selection = new Range(new(0, 0), new(Lines.Last().Count, Lines.Count - 1));
30 | }
31 | }
32 |
33 | private async void TextControl_PointerPressed(object sender, PointerRoutedEventArgs e)
34 | {
35 | IsSuggesting = false;
36 | bool hasfocus = Focus(FocusState.Pointer);
37 | PointerPoint currentpoint = e.GetCurrentPoint(TextControl);
38 | try
39 | {
40 |
41 | if (e.Pointer.PointerDeviceType == PointerDeviceType.Touch)
42 | {
43 | Place start = await PointToPlace(currentpoint.Position);
44 | Place end = new Place(start.iChar, start.iLine);
45 | var matches = Regex.Matches(Lines[start.iLine].LineText, @"\b\w+?\b");
46 | foreach (Match match in matches)
47 | {
48 | int istart = match.Index;
49 | int iend = match.Index + match.Length;
50 | if (start.iChar <= iend && start.iChar >= istart)
51 | {
52 | start.iChar = istart;
53 | end.iChar = iend;
54 | }
55 | }
56 | Selection = new(start, end);
57 | }
58 | else if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse | e.Pointer.PointerDeviceType == PointerDeviceType.Pen)
59 | {
60 | if (currentpoint.Properties.IsLeftButtonPressed)
61 | {
62 | if (IsSelection)
63 | {
64 | Place pos = await PointToPlace(currentpoint.Position);
65 | if (pos > Selection.VisualStart && pos <= Selection.VisualEnd)
66 | {
67 | isDragging = true;
68 | draggedText = SelectedText;
69 | draggedSelection = new(Selection);
70 | //e.Handled = true;
71 | return;
72 | }
73 | }
74 | isLineSelect = currentpoint.Position.X < Width_Left;
75 |
76 | isSelecting = true;
77 | if (!isLineSelect)
78 | {
79 | if (previousPosition != currentpoint.Position)
80 | {
81 | Place start = await PointToPlace(currentpoint.Position);
82 | Place end = await PointToPlace(currentpoint.Position);
83 | Selection = new(start, end);
84 | if (CursorPlaceHistory.Count > 0)
85 | {
86 | if (end.iLine != CursorPlaceHistory.Last().iLine)
87 | CursorPlaceHistory.Add(end);
88 | }
89 | else
90 | {
91 | CursorPlaceHistory.Add(end);
92 | }
93 | }
94 | else
95 | {
96 | Place start = await PointToPlace(previousPosition);
97 | Place end = new Place(start.iChar, start.iLine);
98 | var matches = Regex.Matches(Lines[start.iLine].LineText, string.Join('|', Language.WordSelectionDefinitions));
99 | foreach (Match match in matches)
100 | {
101 | int istart = match.Index;
102 | int iend = match.Index + match.Length;
103 | if (start.iChar <= iend && start.iChar >= istart)
104 | {
105 | start.iChar = istart;
106 | end.iChar = iend;
107 | }
108 | }
109 | Selection = new(start, end);
110 |
111 |
112 |
113 | DoubleClicked?.Invoke(this, new());
114 | }
115 | previousPosition = currentpoint.Position;
116 | }
117 | else
118 | {
119 | Place start = await PointToPlace(currentpoint.Position);
120 | SelectLine(start);
121 | }
122 | isMiddleClickScrolling = false;
123 | previousPosition = currentpoint.Position;
124 | iCharPosition = CursorPlace.iChar;
125 | }
126 | else if (currentpoint.Properties.IsRightButtonPressed)
127 | {
128 | Place rightpress = await PointToPlace(currentpoint.Position);
129 |
130 | Place start = Selection.VisualStart;
131 | Place end = Selection.VisualEnd;
132 | if (IsSelection)
133 | {
134 | if (rightpress <= start || rightpress >= end)
135 | {
136 | Selection = new Range(rightpress);
137 | }
138 | }
139 | else
140 | Selection = new Range(rightpress);
141 | isMiddleClickScrolling = false;
142 | }
143 | else if (currentpoint.Properties.IsXButton1Pressed)
144 | {
145 | if (CursorPlaceHistory.Count > 1)
146 | {
147 | Selection = new Range(CursorPlaceHistory[CursorPlaceHistory.Count - 2]);
148 | CursorPlaceHistory.Remove(CursorPlaceHistory.Last());
149 | }
150 | }
151 | else if (currentpoint.Properties.IsMiddleButtonPressed)
152 | {
153 | if (!isMiddleClickScrolling)
154 | {
155 | if (VerticalScroll.Maximum + Scroll.ActualHeight > Scroll.ActualHeight && HorizontalScroll.Maximum + Scroll.ActualWidth > Scroll.ActualWidth)
156 | {
157 | isMiddleClickScrolling = true;
158 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeAll);
159 | //Cursor = new CoreCursor(CoreCursorType.SizeAll, 1);
160 | middleClickScrollingStartPoint = currentpoint.Position;
161 | DispatcherTimer Timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
162 | Timer.Tick += (a, b) =>
163 | {
164 | if (!isMiddleClickScrolling)
165 | {
166 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
167 | ((DispatcherTimer)a).Stop();
168 | }
169 | else
170 | {
171 | VerticalScroll.Value += middleClickScrollingEndPoint.Y - middleClickScrollingStartPoint.Y;
172 | HorizontalScroll.Value += middleClickScrollingEndPoint.X - middleClickScrollingStartPoint.X;
173 | }
174 | };
175 | Timer.Start();
176 | }
177 | else if (VerticalScroll.Maximum + Scroll.ActualHeight > Scroll.ActualHeight)
178 | {
179 | isMiddleClickScrolling = true;
180 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeNorthSouth);
181 | //Cursor = new CoreCursor(CoreCursorType.SizeNorthSouth, 1);
182 | middleClickScrollingStartPoint = currentpoint.Position;
183 | DispatcherTimer Timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
184 | Timer.Tick += (a, b) =>
185 | {
186 | if (!isMiddleClickScrolling)
187 | {
188 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
189 | ((DispatcherTimer)a).Stop();
190 | }
191 | else
192 | VerticalScroll.Value += middleClickScrollingEndPoint.Y - middleClickScrollingStartPoint.Y;
193 | };
194 | Timer.Start();
195 | }
196 | else if (HorizontalScroll.Maximum + Scroll.ActualWidth > Scroll.ActualWidth)
197 | {
198 | isMiddleClickScrolling = true;
199 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.SizeWestEast);
200 | //Cursor = new CoreCursor(CoreCursorType.SizeWestEast, 1);
201 | middleClickScrollingStartPoint = currentpoint.Position;
202 | DispatcherTimer Timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(100) };
203 | Timer.Tick += (a, b) =>
204 | {
205 | if (!isMiddleClickScrolling)
206 | {
207 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
208 | ((DispatcherTimer)a).Stop();
209 | }
210 | else
211 | HorizontalScroll.Value += middleClickScrollingEndPoint.X - middleClickScrollingStartPoint.X;
212 | };
213 | Timer.Start();
214 | }
215 | }
216 | else isMiddleClickScrolling = false;
217 | }
218 | }
219 | }
220 | catch (Exception ex)
221 | {
222 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
223 | }
224 | e.Handled = true;
225 | }
226 |
227 | private async void TextControl_PointerMoved(object sender, PointerRoutedEventArgs e)
228 | {
229 | try
230 | {
231 | CurrentPointerPoint = e.GetCurrentPoint(TextControl);
232 | if (!isDragging)
233 | if (e.Pointer.PointerDeviceType == PointerDeviceType.Mouse | e.Pointer.PointerDeviceType == PointerDeviceType.Pen)
234 | {
235 | Place place = await PointToPlace(CurrentPointerPoint.Position);
236 | if (isSelecting && CurrentPointerPoint.Properties.IsLeftButtonPressed)
237 | {
238 | if (!isLineSelect)
239 | {
240 | Selection = new Range(Selection.Start, await PointToPlace(CurrentPointerPoint.Position));
241 | }
242 | else
243 | {
244 | place.iChar = Lines[place.iLine].Count;
245 | Selection = new Range(Selection.Start, place);
246 | }
247 | }
248 | else if (isMiddleClickScrolling)
249 | {
250 | middleClickScrollingEndPoint = CurrentPointerPoint.Position;
251 | }
252 | else
253 | {
254 | if (CurrentPointerPoint.Position.X < Width_Left)
255 | {
256 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.Arrow);
257 | }
258 | else if (IsSelection)
259 | {
260 | Place start = Selection.VisualStart;
261 | Place end = Selection.VisualEnd;
262 | if (place < start || place >= end)
263 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
264 | else
265 | {
266 | if (place.iChar < Lines[place.iLine].Count)
267 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.Arrow);
268 | else
269 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
270 | }
271 | }
272 | else
273 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
274 | }
275 | e.Handled = true;
276 | }
277 | }
278 | catch (Exception ex)
279 | {
280 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
281 | }
282 | }
283 |
284 | private async void TextControl_PointerReleased(object sender, PointerRoutedEventArgs e)
285 | {
286 | try
287 | {
288 | PointerPoint currentpoint = e.GetCurrentPoint(TextControl);
289 | Place place = await PointToPlace(currentpoint.Position);
290 |
291 | isLineSelect = false;
292 |
293 | if (isSelecting)
294 | {
295 | isSelecting = false;
296 | e.Handled = true;
297 | TextControl.Focus(FocusState.Pointer);
298 | }
299 |
300 | if (isDragging && place > Selection.VisualStart && place <= Selection.VisualEnd)
301 | {
302 | e.Handled = true;
303 | Selection = new Range(place);
304 | ProtectedCursor = InputSystemCursor.Create(InputSystemCursorShape.IBeam);
305 | Focus(FocusState.Keyboard);
306 | }
307 | else if (isDragging)
308 | {
309 | Focus(FocusState.Pointer);
310 | }
311 | isDragging = false;
312 | }
313 | catch (Exception ex)
314 | {
315 | ErrorOccured?.Invoke(this, new ErrorEventArgs(ex));
316 | }
317 | }
318 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/CodeWriter.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
70 |
71 |
72 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
195 |
200 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
273 |
274 |
275 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
289 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
303 |
304 |
305 |
306 |
314 |
315 |
316 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Fonts/FiraCode.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditorControl-WinUI/Fonts/FiraCode.ttf
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Fonts/JetBrainsMono.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WelterDevelopment/CodeEditorContol-WinUI/9ed9cb5ecc7f1525f1839169eb2a82956de52ad4/CodeEditorControl-WinUI/Fonts/JetBrainsMono.ttf
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Converters.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 | using Microsoft.UI.Xaml.Controls;
3 | using Microsoft.UI.Xaml.Data;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Globalization;
7 |
8 | namespace CodeEditorControl_WinUI;
9 | public class WidthToThickness : IValueConverter
10 | {
11 | public object Convert(object value, Type targetType, object parameter, string culture)
12 | {
13 | double offset = (double)value;
14 | return new Thickness(0, offset, 0, offset);
15 | }
16 |
17 | public object ConvertBack(object value, Type targetType, object parameter, string culture)
18 | {
19 | return 0;
20 | }
21 | }
22 | public class Multiply : IValueConverter
23 | {
24 | public object Convert(object value, Type targetType, object parameter, string culture)
25 | {
26 | double input = double.Parse(value.ToString(), CultureInfo.InvariantCulture);
27 | double factor = double.Parse(parameter.ToString(), CultureInfo.InvariantCulture);
28 | return Math.Max(Math.Min(input * factor, 32), 12);
29 | }
30 |
31 | public object ConvertBack(object value, Type targetType, object parameter, string culture)
32 | {
33 | return 0;
34 | }
35 | }
36 | public class TokenToColor : IValueConverter
37 | {
38 | public object Convert(object value, Type targetType, object parameter, string culture)
39 | {
40 | if (value is Token token)
41 | return EditorOptions.TokenColors[token];
42 | else return EditorOptions.TokenColors[Token.Normal];
43 | }
44 |
45 | public object ConvertBack(object value, Type targetType, object parameter, string culture)
46 | {
47 | return 0;
48 | }
49 | }
50 | public class FocusToVisibility : IValueConverter
51 | {
52 | public object Convert(object value, Type targetType, object parameter, string culture)
53 | {
54 | FocusState state = (FocusState)value;
55 | return state != FocusState.Unfocused ? Visibility.Visible : Visibility.Collapsed;
56 | }
57 |
58 | public object ConvertBack(object value, Type targetType, object parameter, string culture)
59 | {
60 | return 0;
61 | }
62 | }
63 | public class ArgumentsToString : IValueConverter
64 | {
65 | public object Convert(object value, Type targetType, object parameter, string culture)
66 | {
67 | string argstring = "";
68 | List list = (List)value;
69 | foreach (var item in list)
70 | {
71 | string delstart = "";
72 | string delend = "";
73 | switch (item.Delimiters)
74 | {
75 | case "parentheses ": delstart = "("; delend = ")"; break;
76 | case "braces": delstart = "{"; delend = "}"; break;
77 | case "anglebrackets": delstart = "<"; delend = ">"; break;
78 | case "none": delstart = ""; delend = ""; break;
79 | case "brackets": delstart = "["; delend = "]"; break;
80 | default: delstart = "["; delend = "]"; break;
81 | }
82 | argstring += " " + delstart + "..." + delend;
83 | }
84 | return argstring;
85 | }
86 |
87 | public object ConvertBack(object value, Type targetType, object parameter, string culture)
88 | {
89 | return null;
90 | }
91 | }
92 |
93 | public class SuggestionTemplateSelector : DataTemplateSelector
94 | {
95 | public DataTemplate IntelliSenseTemplate { get; set; }
96 | public DataTemplate ArgumentTemplate { get; set; }
97 |
98 | protected override DataTemplate SelectTemplateCore(object item, DependencyObject dependency)
99 | {
100 | if (item is IntelliSense)
101 | {
102 | return IntelliSenseTemplate;
103 | }
104 | else if (item is Parameter)
105 | {
106 | return ArgumentTemplate;
107 | }
108 | else
109 | return null;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Editing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace CodeEditorControl_WinUI;
8 | public class EditAction
9 | {
10 | public EditActionType EditActionType { get; set; }
11 | public string TextState { get; set; }
12 | public string TextInvolved { get; set; }
13 | public Range Selection { get; set; }
14 | }
15 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Enums.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace CodeEditorControl_WinUI;
3 | public enum IndentGuide
4 | {
5 | None, Line, Dashed
6 | }
7 | public enum EditActionType
8 | {
9 | Delete, Paste, Add, Remove
10 | }
11 |
12 | public enum IntelliSenseType
13 | {
14 | Command, Argument,
15 | }
16 |
17 | public enum LexerState
18 | {
19 | Normal, Comment, String
20 | }
21 |
22 | public enum SelectionType
23 | {
24 | Selection, SearchMatch, WordLight
25 | }
26 |
27 | public enum SyntaxErrorType
28 | {
29 | None, Error, Warning, Message
30 | }
31 |
32 | public enum Token
33 | {
34 | Normal, Environment, Command, Function, Keyword, Primitive, Definition, String, Comment, Dimension, Text, Reference, Key, Value, Number, Bracket, Style, Array, Symbol,
35 | Math, Special
36 | }
37 |
38 | public enum VisibleState
39 | {
40 | Visible, StartOfHiddenBlock, Hidden
41 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Numerics;
6 | using System.Runtime.CompilerServices;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using Windows.Foundation;
10 | using Windows.UI;
11 |
12 | namespace CodeEditorControl_WinUI;
13 |
14 | public class Bindable : INotifyPropertyChanged
15 | {
16 | private Dictionary _properties = new Dictionary();
17 |
18 | public event PropertyChangedEventHandler PropertyChanged;
19 |
20 | protected T Get(T defaultVal = default, [CallerMemberName] string name = null)
21 | {
22 | if (!_properties.TryGetValue(name, out object value))
23 | {
24 | value = _properties[name] = defaultVal;
25 | }
26 | return (T)value;
27 | }
28 |
29 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
30 | {
31 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
32 | }
33 |
34 | protected void Set(T value, [CallerMemberName] string name = null)
35 | {
36 | if (Equals(value, Get(value, name)))
37 | return;
38 | _properties[name] = value;
39 | OnPropertyChanged(name);
40 | }
41 | }
42 | public static class Extensions
43 | {
44 | public static Vector2 Center(this Rect rect)
45 | {
46 | return new Vector2((float)rect.X + (float)rect.Width / 2, (float)rect.Y + (float)rect.Height / 2);
47 | }
48 |
49 | public static Color ChangeColorBrightness(this Color color, float correctionFactor)
50 | {
51 | float red = color.R;
52 | float green = color.G;
53 | float blue = color.B;
54 |
55 | if (correctionFactor < 0)
56 | {
57 | correctionFactor = 1 + correctionFactor;
58 | red *= correctionFactor;
59 | green *= correctionFactor;
60 | blue *= correctionFactor;
61 | }
62 | else
63 | {
64 | red = (255 - red) * correctionFactor + red;
65 | green = (255 - green) * correctionFactor + green;
66 | blue = (255 - blue) * correctionFactor + blue;
67 | }
68 |
69 | return Color.FromArgb(color.A, (byte)red, (byte)green, (byte)blue);
70 | }
71 |
72 | public static Color InvertColorBrightness(this Color color)
73 | {
74 | // ToDo: Come up with some fancy way of producing perfect colors for the light theme
75 | float red = color.R;
76 | float green = color.G;
77 | float blue = color.B;
78 |
79 | float lumi = (0.33f * red) + (0.33f * green) + (0.33f * blue);
80 |
81 | red = 255 - lumi + 0.6f * (red - lumi);
82 | green = 255 - lumi + 0.35f * (green - lumi);
83 | blue = 255 - lumi + 0.4f * (blue - lumi);
84 |
85 | return Color.FromArgb(color.A, (byte)red, (byte)green, (byte)blue);
86 | }
87 |
88 | public static System.Drawing.Point ToDrawingPoint(this Windows.Foundation.Point point)
89 | {
90 | return new System.Drawing.Point((int)point.X, (int)point.Y);
91 | }
92 |
93 | public static Windows.Foundation.Point ToFoundationPoint(this System.Drawing.Point point)
94 | {
95 | return new Windows.Foundation.Point(point.X, point.Y);
96 | }
97 |
98 | public static Windows.UI.Color ToUIColor(this System.Drawing.Color color)
99 | {
100 | return Windows.UI.Color.FromArgb(color.A, color.R, color.G, color.B);
101 | }
102 |
103 | public static Vector2 ToVector2(this System.Drawing.Point point)
104 | {
105 | return new Vector2((float)point.X, (float)point.Y);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/IntelliSense.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace CodeEditorControl_WinUI;
4 |
5 | public class IntelliSense : Suggestion
6 | {
7 | public IntelliSense(string text)
8 | {
9 | Name = text;
10 | Token = Token.Command;
11 | }
12 |
13 | public List ArgumentsList { get; set; } = new();
14 | }
15 |
16 | public class Suggestion : Bindable
17 | {
18 | public string Name { get; set; }
19 |
20 | public Token Token { get; set; } = Token.Normal;
21 | public IntelliSenseType IntelliSenseType { get; set; } = IntelliSenseType.Command;
22 | public string Snippet { get; set; } = "";
23 | public string Options { get; set; } = "";
24 | public string Description { get; set; } = "";
25 | }
26 |
27 |
28 | public class Argument : Suggestion
29 | {
30 | public int Number { get; set; }
31 | public bool IsSelected { get => Get(false); set => Set(value); }
32 | public bool Optional { get; set; }
33 | public string Delimiters { get; set; }
34 | public string List { get; set; }
35 | public List Parameters { get; set; }
36 | }
37 |
38 |
39 | public class Parameter : Suggestion
40 | {
41 | }
42 |
43 | public class Constant : Parameter
44 | {
45 | public string Type { get; set; }
46 | }
47 |
48 | public class KeyValue : Parameter
49 | {
50 | public List Values { get; set; } = new();
51 | }
52 | public class BracketPair
53 | {
54 | public BracketPair()
55 | {
56 | }
57 |
58 | public BracketPair(Place open, Place close)
59 | {
60 | iOpen = open;
61 | iClose = close;
62 | }
63 |
64 | public Place iClose { get; set; } = new Place();
65 | public Place iOpen { get; set; } = new Place();
66 | }
67 |
68 | public class SyntaxError
69 | {
70 | public string Description { get; set; } = "";
71 | public int iChar { get; set; } = 0;
72 | public int iLine { get; set; } = 0;
73 | public SyntaxErrorType SyntaxErrorType { get; set; } = SyntaxErrorType.None;
74 | public string Title { get; set; } = "";
75 | }
76 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Language.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Windows.UI;
3 |
4 | namespace CodeEditorControl_WinUI;
5 | public class Language
6 | {
7 | public Language(string language)
8 | {
9 | Name = language;
10 | }
11 |
12 | public List FoldingPairs { get; set; }
13 | public List NestedLanguages { get; set; }
14 | public string Name { get; set; }
15 |
16 | public char[] CommandTriggerCharacters { get; set; } = new char[] { };
17 | public char[] OptionsTriggerCharacters { get; set; } = new char[] { };
18 |
19 | public char[] EscapeSymbols { get; set; } = new char[] { };
20 | public Dictionary RegexTokens { get; set; }
21 | public Dictionary WordTokens { get; set; }
22 | public Dictionary AutoClosingPairs { get; set; } = new();
23 | public List Commands { get; set; }
24 | public List WordSelectionDefinitions { get; set; } = new() { /*language=regex*/ @"\b\w+?\b", };
25 |
26 | public string LineComment { get; set; }
27 |
28 | public bool EnableIntelliSense { get; set; } = false;
29 | }
30 |
31 | public class NestedLanguage
32 | {
33 | public string InnerLanguage { get; set; }
34 | public string RegexEnd { get; set; }
35 | public string RegexStart { get; set; }
36 | }
37 | public static class EditorOptions
38 | {
39 | public static Dictionary TokenColors = new()
40 | {
41 | { Token.Normal, Color.FromArgb(255, 220, 220, 220) },
42 | { Token.Command, Color.FromArgb(255, 50, 130, 210) },
43 | { Token.Function, Color.FromArgb(255, 200, 120, 220) },
44 | { Token.Keyword, Color.FromArgb(255, 50, 130, 210) },
45 | { Token.Environment, Color.FromArgb(255, 50, 190, 150) },
46 | { Token.Comment, Color.FromArgb(255, 40, 190, 90) },
47 | { Token.Key, Color.FromArgb(255, 150, 120, 200) },
48 | { Token.Bracket, Color.FromArgb(255, 100, 140, 220) },
49 | { Token.Reference, Color.FromArgb(255, 180, 140, 40) },
50 | { Token.Math, Color.FromArgb(255, 220, 160, 60) },
51 | { Token.Symbol, Color.FromArgb(255, 140, 200, 240) },
52 | { Token.Style, Color.FromArgb(255, 220, 130, 100) },
53 | { Token.String, Color.FromArgb(255, 220, 130, 100) },
54 | { Token.Special, Color.FromArgb(255, 50, 190, 150) },
55 | { Token.Number, Color.FromArgb(255, 180, 220, 180) },
56 | { Token.Array, Color.FromArgb(255, 200, 100, 80) },
57 | { Token.Primitive, Color.FromArgb(255, 230, 120, 100) },
58 | };
59 | }
60 | public class TokenDefinition : Bindable
61 | {
62 | public Token Token { get => Get(Token.Normal); set => Set(value); }
63 | public Color Color
64 | {
65 | get => Get(Color.FromArgb(255, 220, 220, 220));
66 | set
67 | {
68 | Set(value);
69 | try
70 | {
71 | EditorOptions.TokenColors[Token] = value;
72 | CodeWriter.Current?.RedrawText();
73 | }
74 | catch { }
75 | }
76 | }
77 | }
78 | public class SyntaxFolding
79 | {
80 | public string RegexEnd { get; set; }
81 | public string RegexStart { get; set; }
82 | public int MatchingGroup { get; set; } = -1;
83 | public List FoldingIgnoreWords { get; set; }
84 | }
85 |
86 | public static class Languages
87 | {
88 |
89 | public static List LanguageList = new()
90 | {
91 | new("Lua")
92 | {
93 | FoldingPairs = new()
94 | {
95 | new() { RegexStart = /*language=regex*/ @"\b(function|for|while|if)\b", RegexEnd = /*language=regex*/ @"\bend\b" },
96 | },
97 | RegexTokens = new()
98 | {
99 | { Token.Math, /*language=regex*/ @"\b(math)\.(pi|a?tan|atan2|tanh|a?cos|cosh|a?sin|sinh|max|pi|min|ceil|floor|(fr|le)?exp|pow|fmod|modf|random(seed)?|sqrt|log(10)?|deg|rad|abs)\b" },
100 | { Token.Array, /*language=regex*/ @"\b((table)\.(insert|concat|sort|remove|maxn)|(string)\.(insert|sub|rep|reverse|format|len|find|byte|char|dump|lower|upper|g?match|g?sub|format|formatters))\b" },
101 | { Token.Symbol, /*language=regex*/ @"[:=<>,.!?&%+\|\-*\/\^~;]" },
102 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
103 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*|-?\d*\.\d+([eE][\-+]?\d+)?|-?\d+?" },
104 | { Token.String, /*language=regex*/"\\\".*?\\\"|'.*?'" },
105 | { Token.Comment, /*language=regex*/"\\\"[^\\\"]*\\\" | --.*?\\\n" },
106 | },
107 | WordTokens = new()
108 | {
109 | { Token.Keyword, new string[] { "local", "true", "false", "in", "else", "not", "or", "and", "then", "nil", "end", "do", "repeat", "goto", "until", "return", "break" } },
110 | { Token.Environment, new string[] { "function", "end", "if", "elseif", "else", "while", "for", } },
111 | { Token.Function, new string[] { "#", "assert", "collectgarbage", "dofile", "_G", "getfenv", "ipairs", "load", "loadstring", "pairs", "pcall", "print", "rawequal", "rawget", "rawset", "select", "setfenv", "_VERSION", "xpcall", "module", "require", "tostring", "tonumber", "type", "rawset", "setmetatable", "getmetatable", "error", "unpack", "next", } }
112 | },
113 | },
114 | new("Markdown")
115 | {
116 | FoldingPairs = new()
117 | {
118 |
119 | },
120 | RegexTokens = new()
121 | {
122 | { Token.Environment, /*language=regex*/ @"^\s*?#+? .*" },
123 | { Token.Keyword, /*language=regex*/ @"^[\w ]*?(?=>)" },
124 | { Token.Command, /*language=regex*/ @"(?<=<\/|<)\w+?\b(?=.*?\/?>)" },
125 | { Token.Function, /*language=regex*/ @"\[.*?\]" },
126 | { Token.Key, /*language=regex*/ @"(?<=\s)\w+?\s*?(?==)" },
127 | { Token.Comment, /*language=regex*/ @"^\s*?> .*" },
128 | { Token.String, /*language=regex*/ @"'.*?'" },
129 | { Token.Symbol, /*language=regex*/ @"[:=<>,.!?&%+\|\-*\/\^~;´`]" },
130 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
131 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*\b|-?\d*\.\d+([eE][\-+]?\d+)?\b|-?\d+?\b" },
132 |
133 | },
134 | WordTokens = new()
135 | {
136 |
137 | },
138 | },
139 | new("Xml")
140 | {
141 | FoldingPairs = new()
142 | {
143 |
144 | },
145 | RegexTokens = new()
146 | {
147 | { Token.Command, /*language=regex*/ @"?.+?/?>" },
148 | { Token.String, /*language=regex*/"\\\".*?\\\"|'.*?'" },
149 | { Token.Symbol, /*language=regex*/ @"[:=<>,.!?&%+\|\-*\/\^~;´`]" },
150 | { Token.Bracket, /*language=regex*/ @"[\[\]\(\)\{\}]" },
151 | { Token.Number, /*language=regex*/ @"0[xX][0-9a-fA-F]*|-?\d*\.\d+([eE][\-+]?\d+)?|-?\d+?" },
152 | },
153 | WordTokens = new()
154 | {
155 |
156 | },
157 | },
158 | new("Text")
159 | {
160 | FoldingPairs = new()
161 | {
162 |
163 | },
164 | RegexTokens = new()
165 | {
166 |
167 | },
168 | WordTokens = new()
169 | {
170 |
171 | },
172 | },
173 | };
174 | }
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Line.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text.RegularExpressions;
5 |
6 | namespace CodeEditorControl_WinUI;
7 |
8 | public class Char : CharElement
9 | {
10 | public Char(char c)
11 | {
12 | C = c;
13 | }
14 | }
15 |
16 | public class CharElement : Bindable
17 | {
18 | public char C { get => Get(' '); set => Set(value); }
19 | public Token T { get => Get(Token.Normal); set => Set(value); }
20 | }
21 |
22 | /// Currently unused. ToDo: Render whole words at once
23 | public class CharGroup : CharElement
24 | {
25 | public CharGroup(Char[] chars)
26 | {
27 | C = chars;
28 | }
29 |
30 | public new Char[] C { get => Get(new Char[] { }); set => Set(value); }
31 | }
32 | public class Line : Bindable
33 | {
34 | public VisibleState VisibleState = VisibleState.Visible;
35 |
36 | private string lastsavedtext = null;
37 |
38 | public Line(Language language = null)
39 | {
40 | if (language != null)
41 | Language = language;
42 | }
43 |
44 | public List Chars { get => Get(new List()); set => Set(value); }
45 |
46 | public List> WrappedLines { get => Get(new List>()); set => Set(value); }
47 |
48 | public int Count
49 | {
50 | get { return Chars.Count; }
51 | }
52 |
53 | public Folding Folding { get => Get(new Folding()); set => Set(value); }
54 |
55 | public string FoldingEndMarker { get; set; }
56 |
57 | public string FoldingStartMarker { get; set; }
58 |
59 | public int iLine { get => LineNumber - 1; }
60 |
61 | public int Indents { get { return LineText != null ? LineText.Count(x => x == '\t') : 0; } }
62 |
63 | public int GetLineWraps(int maxchar, int tablength, int wrappingindent)
64 | {
65 |
66 | int linewraps = 0;
67 | int indents = Indents;
68 | int iWrappingChar = 0;
69 | List wrappedLine = new List();
70 |
71 | for (int iChar = 0; iChar < Count - 1; iChar++)
72 | {
73 | int iWrappingEndPosition = (linewraps + 1) * maxchar - (wrappingindent * linewraps) - (indents * (tablength - 1) * (linewraps + 1));
74 |
75 | if (iChar > 0 && iChar < iWrappingEndPosition)
76 | iWrappingChar++;
77 |
78 | if (iChar == iWrappingEndPosition)
79 | {
80 | iWrappingChar = wrappingindent;
81 | linewraps++;
82 | }
83 | }
84 |
85 | return linewraps;
86 | }
87 |
88 | public void CalculateWrappedLines(int iVisibleChars, int TabLength = 2, int WrapIndent = 3)
89 | {
90 | WrappedLines.Clear();
91 | int lastChar = Count - 1;
92 | int indents = 0;
93 | int linewraps = 0;
94 | int iWrappingChar = 0;
95 | int iLastWrappingPosition = 0;
96 | if (lastChar == -1)
97 | {
98 | WrappedLines.Add(new(Chars));
99 | }
100 | else
101 | for (int iChar = 0; iChar <= lastChar; iChar++)
102 | {
103 | Char c = this[iChar];
104 |
105 | if (c.C == '\t')
106 | {
107 | //x = Width_Left + CharWidth * (iChar + indents * (TabLength - 1) - iCharOffset);
108 | indents += 1;
109 | }
110 | else if (iChar >= indents * (TabLength - 1))
111 | {
112 | int maxchar = iVisibleChars - 1;
113 | //if (iChar + indents * (TabLength - 1) - iCharOffset < maxchar)
114 | //{
115 | // x = Width_Left + CharWidth * (iChar + indents * (TabLength - 1) - iCharOffset);
116 | //}
117 | //else
118 | //{
119 | int iWrappingEndPosition = (linewraps + 1) * maxchar - (WrapIndent * linewraps) - (indents * (TabLength - 1) * (linewraps + 1));
120 |
121 | iWrappingEndPosition = Math.Min(iWrappingEndPosition, lastChar);
122 |
123 | if (iChar > 0 && iChar <= iWrappingEndPosition)
124 | iWrappingChar++;
125 |
126 | if (iChar == iWrappingEndPosition)
127 | {
128 | iWrappingChar = WrapIndent;
129 | linewraps++;
130 | WrappedLines.Add(new(Chars.GetRange(iLastWrappingPosition, iChar - iLastWrappingPosition + 1)));
131 | iLastWrappingPosition = iChar + 1;
132 | // args.DrawingSession.FillRectangle(0, y, Width_Left - Width_TextIndent, CharHeight, Color_LeftBackground);
133 | //wrapindent = 1;
134 | }
135 |
136 |
137 | }
138 | }
139 | }
140 |
141 | public bool IsFoldEnd { get => Get(false); set => Set(value); }
142 | public bool IsFoldInner { get => Get(false); set => Set(value); }
143 | public bool IsFoldInnerEnd { get => Get(false); set => Set(value); }
144 | public bool IsFoldStart { get => Get(false); set => Set(value); }
145 | public bool IsUnsaved { get => Get(false); set { Set(value); } }
146 | public Language Language { get => Get(); set { Set(value); SetLineText(LineText); } }
147 | public int LineNumber { get => Get(0); set => Set(value); }
148 |
149 | public void Save() { lastsavedtext = LineText; IsUnsaved = false; }
150 |
151 | public string LineText
152 | {
153 | get => Get("");
154 | set
155 | {
156 | IsUnsaved = value != lastsavedtext;
157 |
158 | Set(value);
159 |
160 | }
161 | }
162 |
163 | public void SetLineText(string value)
164 | {
165 | LineText = value;
166 |
167 | //await Task.Run(() =>
168 | //{
169 | Chars = FormattedText(value);
170 | //IsFoldStart = FoldableStart(value);
171 | //IsFoldInnerEnd = FoldableEnd(value);
172 | //IsFoldInner = !IsFoldStart && !IsFoldInnerEnd;
173 | //});
174 | }
175 |
176 | public void AddToLineText(string value)
177 | {
178 | LineText += value;
179 |
180 | //Task.Run(() =>
181 | //{
182 | Chars = FormattedText(LineText);
183 | //IsFoldStart = FoldableStart(value);
184 | //IsFoldInnerEnd = FoldableEnd(value);
185 | //IsFoldInner = !IsFoldStart && !IsFoldInnerEnd;
186 | //}).Wait();
187 |
188 | }
189 |
190 | public int WordWrapStringsCount { get; internal set; }
191 |
192 | public Char this[int index]
193 | {
194 | get
195 | {
196 | return Chars[index];
197 | }
198 | set
199 | {
200 | Chars[index] = value;
201 | }
202 | }
203 |
204 | public void Add(Char item)
205 | {
206 | Chars.Add(item);
207 | }
208 |
209 | public virtual void AddRange(IEnumerable collection)
210 | {
211 | //Chars.AddRange(collection);
212 | }
213 |
214 | public void Clear()
215 | {
216 | Chars.Clear();
217 | }
218 |
219 | public bool Contains(Char item)
220 | {
221 | return Chars.Contains(item);
222 | }
223 |
224 | public void CopyTo(Char[] array, int arrayIndex)
225 | {
226 | Chars.CopyTo(array, arrayIndex);
227 | }
228 |
229 | public List FormattedText(string text)
230 | {
231 | List groups = new();
232 |
233 | groups = text.Select(x => new Char(x)).ToList();
234 |
235 | if (Language.RegexTokens != null)
236 | foreach (var token in Language.RegexTokens)
237 | {
238 | MatchCollection mc = Regex.Matches(text, token.Value);
239 | foreach (Match match in mc)
240 | {
241 | for (int i = match.Index; i < match.Index + match.Length; i++)
242 | {
243 | groups[i].T = token.Key;
244 | }
245 | }
246 | }
247 |
248 | if (Language.WordTokens != null)
249 | foreach (var token in Language.WordTokens)
250 | {
251 | var list = token.Value.ToList();
252 | for (int i = 0; i < list.Count; i++)
253 | {
254 | list[i] = list[i].Replace(@"\", @"\\");
255 | }
256 | string pattern;
257 | if (Language.Name == "ConTeXt")
258 | pattern = string.Join(@"\b|", list) + @"\b";
259 | else
260 | pattern = @"\b" + string.Join(@"\b|\b", list) + @"\b";
261 | MatchCollection mc = Regex.Matches(text, pattern);
262 | foreach (Match match in mc)
263 | {
264 | for (int i = match.Index; i < match.Index + match.Length; i++)
265 | {
266 | groups[i].T = token.Key;
267 | }
268 | }
269 | }
270 |
271 | if (!string.IsNullOrEmpty(Language.LineComment))
272 | {
273 | int commentIndex = text.IndexOf(Language.LineComment);
274 | if (commentIndex > -1)
275 | {
276 | if (commentIndex > 0 && Language.EscapeSymbols.Contains(groups[commentIndex - 1].C))
277 | {
278 |
279 | }
280 | else
281 | for (int i = commentIndex; i < text.Count(); i++)
282 | {
283 | groups[i].T = Token.Comment;
284 | }
285 | }
286 | }
287 |
288 | return new(groups);
289 | }
290 |
291 | public IEnumerator GetEnumerator()
292 | {
293 | return Chars.GetEnumerator();
294 | }
295 |
296 | public int IndexOf(Char item)
297 | {
298 | return Chars.IndexOf(item);
299 | }
300 |
301 | public void Insert(int index, Char item)
302 | {
303 | Chars.Insert(index, item);
304 | }
305 |
306 | public bool Remove(Char item)
307 | {
308 | return Chars.Remove(item);
309 | }
310 |
311 | public void RemoveAt(int index)
312 | {
313 | Chars.RemoveAt(index);
314 | }
315 |
316 | internal int GetWordWrapStringFinishPosition(int v, Line line)
317 | {
318 | return 0;
319 | }
320 |
321 | internal int GetWordWrapStringIndex(int iChar)
322 | {
323 | return 0;
324 | }
325 |
326 | internal int GetWordWrapStringStartPosition(object v)
327 | {
328 | return 0;
329 | }
330 |
331 | private bool FoldableEnd(string text)
332 | {
333 | if (Language.FoldingPairs != null)
334 | foreach (SyntaxFolding syntaxFolding in Language.FoldingPairs)
335 | {
336 | var match = Regex.Match(text, syntaxFolding.RegexEnd);
337 | if (match.Success)
338 | {
339 | return true;
340 | }
341 | }
342 | return false;
343 | }
344 |
345 | private bool FoldableStart(string text)
346 | {
347 | if (Language.FoldingPairs != null)
348 | foreach (SyntaxFolding syntaxFolding in Language.FoldingPairs)
349 | {
350 | var match = Regex.Match(text, syntaxFolding.RegexStart);
351 | if (match.Success)
352 | {
353 | return true;
354 | }
355 | }
356 | return false;
357 | }
358 | }
359 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Range.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Input;
3 |
4 | namespace CodeEditorControl_WinUI;
5 | public class Place : IEquatable
6 | {
7 | public int iChar = 0;
8 | public int iLine = 0;
9 |
10 | public Place()
11 | {
12 | }
13 |
14 | public Place(Place oldplace)
15 | {
16 | this.iChar = oldplace.iChar;
17 | this.iLine = oldplace.iLine;
18 | }
19 |
20 | public Place(int iChar, int iLine)
21 | {
22 | this.iChar = iChar;
23 | this.iLine = iLine;
24 | }
25 |
26 | public static Place Empty
27 | {
28 | get { return new Place(); }
29 | }
30 |
31 | public static bool operator !=(Place p1, Place p2)
32 | {
33 | return !p1.Equals(p2);
34 | }
35 |
36 | public static Place operator +(Place p1, Place p2)
37 | {
38 | return new Place(p1.iChar + p2.iChar, p1.iLine + p2.iLine);
39 | }
40 |
41 | public static Place operator +(Place p1, int c2)
42 | {
43 | return new Place(p1.iChar + c2, p1.iLine);
44 | }
45 | public static Place operator -(Place p1, int c2)
46 | {
47 | return new Place(p1.iChar - c2, p1.iLine);
48 | }
49 |
50 | public static bool operator <(Place p1, Place p2)
51 | {
52 | if (p1.iLine < p2.iLine) return true;
53 | if (p1.iLine > p2.iLine) return false;
54 | if (p1.iChar < p2.iChar) return true;
55 | return false;
56 | }
57 |
58 | public static bool operator <=(Place p1, Place p2)
59 | {
60 | if (p1.Equals(p2)) return true;
61 | if (p1.iLine < p2.iLine) return true;
62 | if (p1.iLine > p2.iLine) return false;
63 | if (p1.iChar < p2.iChar) return true;
64 | return false;
65 | }
66 |
67 | public static bool operator ==(Place p1, Place p2)
68 | {
69 | return p1.Equals(p2);
70 | }
71 |
72 | public static bool operator >(Place p1, Place p2)
73 | {
74 | if (p1.iLine > p2.iLine) return true;
75 | if (p1.iLine < p2.iLine) return false;
76 | if (p1.iChar > p2.iChar) return true;
77 | return false;
78 | }
79 |
80 | public static bool operator >=(Place p1, Place p2)
81 | {
82 | if (p1.Equals(p2)) return true;
83 | if (p1.iLine > p2.iLine) return true;
84 | if (p1.iLine < p2.iLine) return false;
85 | if (p1.iChar > p2.iChar) return true;
86 | return false;
87 | }
88 |
89 | public bool Equals(Place other)
90 | {
91 | return iChar == other.iChar && iLine == other.iLine;
92 | }
93 |
94 | public override bool Equals(object obj)
95 | {
96 | return (obj is Place) && Equals((Place)obj);
97 | }
98 |
99 | public override int GetHashCode()
100 | {
101 | return iChar.GetHashCode() ^ iLine.GetHashCode();
102 | }
103 |
104 | public void Offset(int dx, int dy)
105 | {
106 | iChar += dx;
107 | iLine += dy;
108 | }
109 |
110 | public override string ToString()
111 | {
112 | return "(" + (iLine + 1) + "," + (iChar + 1) + ")";
113 | }
114 | }
115 |
116 | public class RelayCommand : ICommand
117 | {
118 | private readonly Func _canExecute;
119 | private readonly Action _execute;
120 |
121 | public RelayCommand(Action execute)
122 | : this(execute, null)
123 | {
124 | }
125 |
126 | public RelayCommand(Action execute, Func canExecute)
127 | {
128 | _execute = execute ?? throw new ArgumentNullException("execute");
129 | _canExecute = canExecute;
130 | }
131 |
132 | public event EventHandler CanExecuteChanged;
133 |
134 | public bool CanExecute(object parameter)
135 | {
136 | return _canExecute == null ? true : _canExecute();
137 | }
138 |
139 | public void Execute(object parameter)
140 | {
141 | _execute();
142 | }
143 |
144 | public void RaiseCanExecuteChanged()
145 | {
146 | CanExecuteChanged?.Invoke(this, EventArgs.Empty);
147 | }
148 | }
149 |
150 | public class Range : Bindable
151 | {
152 | public Range(Range range)
153 | {
154 | Start = range.Start;
155 | End = range.End;
156 | }
157 | public Range(Place place)
158 | {
159 | Start = place ?? new Place();
160 | End = place ?? new Place();
161 | }
162 |
163 | public Range(Place start, Place end)
164 | {
165 | Start = start;
166 | End = end;
167 | }
168 |
169 | public Range()
170 | {
171 | }
172 |
173 | public static Range operator +(Range p1, int c2)
174 | {
175 | return new Range(p1.Start + c2, p1.End + c2);
176 | }
177 | public static Range operator -(Range p1, int c2)
178 | {
179 | return new Range(p1.Start - c2, p1.End - c2);
180 | }
181 |
182 | public Place End { get => Get(new Place()); set => Set(value); }
183 | public Place Start { get => Get(new Place()); set => Set(value); }
184 |
185 | public Place VisualEnd { get => End > Start ? new(End) : new(Start); }
186 | public Place VisualStart { get => End > Start ? new(Start) : new(End); }
187 |
188 | public override string ToString() => Start.ToString() + " -> " + End.ToString();
189 | }
190 |
191 | public class Folding : Bindable
192 | {
193 | public string Name { get => Get(null); set => Set(value); }
194 | public int Endline { get => Get(-1); set => Set(value); }
195 | public int StartLine { get => Get(-1); set => Set(value); }
196 | }
197 |
198 | public class HighlightRange
199 | {
200 | public Place End { get; set; }
201 | public Place Start { get; set; }
202 | }
203 |
--------------------------------------------------------------------------------
/CodeEditorControl-WinUI/Models/Search.cs:
--------------------------------------------------------------------------------
1 | namespace CodeEditorControl_WinUI;
2 | public class SearchMatch
3 | {
4 | public int iChar { get; set; }
5 | public int iLine { get; set; }
6 | public string Match { get; set; }
7 | }
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 WelterDevelopment
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # CodeEditorControl-WinUI
3 | Win2D-based code editor control for WinUI 3.
4 |
5 | At this stage, this is only a proof of concept.
6 | I tried to port https://github.com/PavelTorgashov/FastColoredTextBox line by line which was a huge pain so I had to stop. The codebase is just too huge and hard to read.
7 |
8 | I took some inspiration and created my own Win2d-based control. Feel free to contibute! Would be nice to have a fast full-featured text editor for WinAppSDK / MAUI!
9 |
10 | ## Screenshot of the TestApp
11 | Stuff that works:
12 | - Text selection & beam placement (inputs: PointerPress & Up/Down/Left/Right keys)
13 | - Basic text editing (char insertion, back key, delete key, enter key)
14 | - Basic copy & paste logic
15 | - Scrolling (vertical & horizontal)
16 | - Text, FontSize, Theme and TabLength are two-way bindable DependencyProperties
17 | - Proof-of-Concept syntax highlighting for ConTeXt as a static Language Definition within the Control
18 | - Syntax highligting example for a Lua file in the TestApp
19 | - Actions (right-click menu & KeyboardAccelerators)
20 | - Middle-click scrolling
21 | - Search and highlight
22 | - Drag and drop
23 | - Error/Warning/Message/SearchMatch markers on the line and on the vertical ScrollBar
24 | - Recource-intensive work does not block the UI thread: Setting the Text, Pasting large chunks of Lines, Changing the Code Language
25 |
26 | 
27 |
28 | 
29 |
30 | ## ToDo
31 |
32 | - Line wrapping
33 | - Text folding
34 | - Text-wide instead of line-wise regexing (respectively lexer states for multiline-comment handling)
35 | - Incremental regexing when Text source changes (Regex and draw the VisibleLines immediately, then regex the rest in blocks)
36 | - IntelliSense for commands and arguments
37 | - Find and highlight matching bracket/parenthesis/braces pairs, auto-close pairs
38 | - Generalize the syntax highlighting and IntelliSense for more (user-definable) languages
39 | - Minimap
40 | - Breakpoints and breakpoint line highlighting
41 | - Word/Keyword/Variable highlighting
42 | - Multi-cursor selection and editing
43 |
--------------------------------------------------------------------------------