├── .gitattributes
├── .gitignore
├── AndroidTests
└── AndroidTests.csproj
├── Hexa.NET.ImGui.Widgets.Extras
├── Globals.cs
├── Hexa.NET.ImGui.Widgets.Extras.csproj
├── ImGuiBezierWidget.cs
├── ImGuiCurveEditor.cs
└── TextEditor
│ ├── Breakpoint.cs
│ ├── CaseInsensitiveComparer.cs
│ ├── ColorRGBA.cs
│ ├── CursorState.cs
│ ├── Highlight
│ └── CSharp
│ │ └── CSharpSyntaxHighlight.cs
│ ├── ImGui.cs
│ ├── JumpTarget.cs
│ ├── LineIndexSpan.cs
│ ├── NewLineType.cs
│ ├── Panels
│ └── ExplorerSidePanel.cs
│ ├── SearchResult.cs
│ ├── SidePanel.cs
│ ├── SyntaxHighlight.cs
│ ├── SyntaxHighlightDefaults.cs
│ ├── SyntaxHighlightDefinition.cs
│ ├── TextDrawData.cs
│ ├── TextEditOp.cs
│ ├── TextEditor.cs
│ ├── TextEditorTab.cs
│ ├── TextEditorWindow.cs
│ ├── TextHighlightSpan.cs
│ ├── TextHighlightSpanStartComparer.cs
│ ├── TextHistory.cs
│ ├── TextHistoryEntry.cs
│ ├── TextSelection.cs
│ ├── TextSource.cs
│ ├── TextSpan.cs
│ └── TextSpanStartComparer.cs
├── Hexa.NET.ImGui.Widgets.sln
├── Hexa.NET.ImGui.Widgets
├── ComboHelper.cs
├── Dialogs
│ ├── AscendingComparer.cs
│ ├── CompareByDateModifiedComparer.cs
│ ├── CompareByNameComparer.cs
│ ├── CompareBySizeComparer.cs
│ ├── CompareByTypeComparer.cs
│ ├── Dialog.cs
│ ├── DialogManager.cs
│ ├── DialogMessageBox.cs
│ ├── DialogResult.cs
│ ├── FileDialogBase.cs
│ ├── FileIconHelper.cs
│ ├── FileSystemHelper.cs
│ ├── FileSystemItem.cs
│ ├── IDialog.cs
│ ├── IFileSystemItem.cs
│ ├── IPopup.cs
│ ├── ImGuiFileView.cs
│ ├── Modal.cs
│ ├── MultiSelection.cs
│ ├── OpenFileDialog.cs
│ ├── OpenFolderDialog.cs
│ ├── PathValidator.cs
│ ├── PopupManager.cs
│ ├── RenameFileDialog.cs
│ ├── SaveFileDialog.cs
│ ├── SearchFilterDate.cs
│ ├── SearchFilterSize.cs
│ ├── SearchOptions.cs
│ └── SearchOptionsFlags.cs
├── Extensions
│ ├── EnumExtension.cs
│ ├── ListExtensions.cs
│ └── SpanHelper.cs
├── Globals.cs
├── Hexa.NET.ImGui.Widgets.csproj
├── IImGuiWindow.cs
├── IUIElement.cs
├── ImGuiAnimationHelper.cs
├── ImGuiBreadcrumb.cs
├── ImGuiButton.cs
├── ImGuiFileTreeView.cs
├── ImGuiGC.cs
├── ImGuiName.cs
├── ImGuiProgressBar.cs
├── ImGuiSpinner.cs
├── ImGuiSplitter.cs
├── ImGuiTreeNode.cs
├── ImGuiWidgetFlameGraph.cs
├── ImWindow.cs
├── ImageHelper.cs
├── Imaging
│ └── ImageHelper.cs
├── MaterialIcons.cs
├── MessageBox.cs
├── MessageBoxResult.cs
├── MessageBoxType.cs
├── MessageBoxes.cs
├── Text
│ └── StrBuilderExtensions.cs
├── TextHelper.cs
├── TooltipHelper.cs
├── TryUtils.cs
├── WidgetManager.cs
├── WidgetStyle.cs
└── assets
│ └── fileTypes.json
├── LICENSE.txt
├── PerformanceTests
├── PerformanceTests.csproj
└── Program.cs
├── README.md
├── TestApp
├── ImGuiManager.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── ResizedEventArgs.cs
├── SDLNativeContext.cs
├── TestApp.csproj
├── Time.cs
├── WidgetDemo.cs
├── assets
│ └── fonts
│ │ ├── MaterialSymbolsRounded.ttf
│ │ ├── arial.ttf
│ │ └── arialuni.TTF
└── test.txt
└── TextEditor
├── ImGuiManager.cs
├── Program.cs
├── ResizedEventArgs.cs
├── SDLNativeContext.cs
├── TextEditor.csproj
├── Time.cs
├── WidgetDemo.cs
└── assets
└── fonts
├── MaterialSymbolsRounded.ttf
├── arial.ttf
└── arialuni.ttf
/.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
364 |
365 | # JetBrains Rider
366 | .idea/
367 |
--------------------------------------------------------------------------------
/AndroidTests/AndroidTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0-android
6 | enable
7 | enable
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/Globals.cs:
--------------------------------------------------------------------------------
1 | global using static Hexa.NET.Utilities.Utils;
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/Hexa.NET.ImGui.Widgets.Extras.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0;net9.0-android;net8.0;net8.0-android
5 | enable
6 | enable
7 | true
8 |
9 | 1.0.6
10 | 1.0.6
11 |
12 | Hexa.NET.ImGui.Widgets is a comprehensive library of custom widgets for the ImGui graphical user interface library. This package includes a variety of pre-built widgets that enhance the functionality and usability of ImGui in your .NET applications. Each widget is designed to be easy to integrate, with consistent styling and behavior. This library is an extension of the Hexa.NET.ImGui wrapper, providing additional UI components for a seamless user experience.
13 |
14 |
15 | Hexa.NET, ImGui, GUI, Widgets, UI, User Interface, .NET, C#, Custom Widgets, HexaNET, ImGuiWrapper
16 |
17 | Juna Meinhold
18 | Copyright (c) 2024 Juna Meinhold
19 | https://github.com/HexaEngine/Hexa.NET.ImGui
20 | https://github.com/HexaEngine/Hexa.NET.ImGui
21 | git
22 | LICENSE.txt
23 | README.md
24 |
25 | true
26 | $(NoWarn);1591
27 |
28 | true
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/Breakpoint.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using System;
4 |
5 | public class BreakpointComparer : IComparer
6 | {
7 | public static readonly BreakpointComparer Instance = new();
8 |
9 | public int Compare(Breakpoint x, Breakpoint y)
10 | {
11 | return x.Line.CompareTo(y.Line);
12 | }
13 | }
14 |
15 | public struct Breakpoint : IEquatable
16 | {
17 | public int Line;
18 | public bool Enabled;
19 |
20 | public Breakpoint(int line, bool enabled)
21 | {
22 | Line = line;
23 | Enabled = enabled;
24 | }
25 |
26 | public static readonly Breakpoint Invalid = new(-1, false);
27 |
28 | public override readonly bool Equals(object? obj)
29 | {
30 | return obj is Breakpoint breakpoint && Equals(breakpoint);
31 | }
32 |
33 | public readonly bool Equals(Breakpoint other)
34 | {
35 | return Line == other.Line;
36 | }
37 |
38 | public override readonly int GetHashCode()
39 | {
40 | return HashCode.Combine(Line);
41 | }
42 |
43 | public static bool operator ==(Breakpoint left, Breakpoint right)
44 | {
45 | return left.Equals(right);
46 | }
47 |
48 | public static bool operator !=(Breakpoint left, Breakpoint right)
49 | {
50 | return !(left == right);
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/CaseInsensitiveComparer.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using System.Collections.Generic;
4 |
5 | public readonly struct CaseInsensitiveComparer : IEqualityComparer
6 | {
7 | public static readonly CaseInsensitiveComparer Default = new();
8 |
9 | public readonly bool Equals(char x, char y)
10 | {
11 | return char.ToLowerInvariant(x) == char.ToLowerInvariant(y);
12 | }
13 |
14 | public readonly int GetHashCode(char obj)
15 | {
16 | return char.ToLowerInvariant(obj).GetHashCode();
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/ColorRGBA.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using System.Numerics;
4 | using System.Xml.Serialization;
5 |
6 | [XmlRoot("Color")]
7 | public struct ColorRGBA
8 | {
9 | [XmlAttribute("r")]
10 | public float R;
11 |
12 | [XmlAttribute("g")]
13 | public float G;
14 |
15 | [XmlAttribute("b")]
16 | public float B;
17 |
18 | [XmlAttribute("a")]
19 | public float A;
20 |
21 | public ColorRGBA(float r, float g, float b, float a)
22 | {
23 | R = r;
24 | G = g;
25 | B = b;
26 | A = a;
27 | }
28 |
29 | public ColorRGBA(Vector4 color)
30 | {
31 | R = color.X;
32 | G = color.Y;
33 | B = color.Z;
34 | A = color.W;
35 | }
36 |
37 | public ColorRGBA(uint color)
38 | {
39 | R = (color >> 24 & 0xff) / (float)byte.MaxValue;
40 | G = (color >> 16 & 0xff) / (float)byte.MaxValue;
41 | B = (color >> 8 & 0xff) / (float)byte.MaxValue;
42 | A = (color & 0xff) / (float)byte.MaxValue;
43 | }
44 |
45 | public static implicit operator Vector4(ColorRGBA c)
46 | {
47 | return new(c.R, c.G, c.B, c.A);
48 | }
49 |
50 | public static implicit operator ColorRGBA(uint color)
51 | {
52 | return new(color);
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/CursorState.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | public struct CursorState
4 | {
5 | public int Index;
6 | public int Line;
7 | public int Column;
8 |
9 | public CursorState(int index, int line, int column)
10 | {
11 | Index = index;
12 | Line = line;
13 | Column = column;
14 | }
15 |
16 | public static readonly CursorState NewLineLF = new(1, 1, 0);
17 | public static readonly CursorState NewLineCR = new(1, 1, 0);
18 | public static readonly CursorState NewLineCRLF = new(2, 1, 0);
19 | public static readonly CursorState Invalid = new(-1, -1, -1);
20 |
21 | public static CursorState FromOffset(int offset)
22 | {
23 | return new CursorState(offset, 0, offset);
24 | }
25 |
26 | public static CursorState FromIndex(int index, TextSource source)
27 | {
28 | int line = 0;
29 | int column = 0;
30 | for (; line < source.LineCount; line++)
31 | {
32 | var lineSpan = source.Lines[line];
33 | if (lineSpan.Start <= index && lineSpan.End >= index)
34 | {
35 | column = index - lineSpan.Start;
36 | break;
37 | }
38 | }
39 |
40 | return new(index, line, column);
41 | }
42 |
43 | public static CursorState FromLineColumn(int line, int column, TextSource source)
44 | {
45 | var lineSpan = source.Lines[line];
46 | var index = lineSpan.Start + column;
47 | return new(index, line, column);
48 | }
49 |
50 | public static CursorState operator ++(CursorState state)
51 | {
52 | int newIndex = state.Index + 1;
53 | int newColumn = state.Column + 1;
54 | return new(newIndex, state.Line, newColumn);
55 | }
56 |
57 | public static CursorState operator --(CursorState state)
58 | {
59 | int newLine = state.Line;
60 | int newColumn = state.Column - 1;
61 | if (newColumn < 0)
62 | {
63 | newLine--;
64 | newColumn = 0;
65 | }
66 | return new(state.Index - 1, newLine, newColumn);
67 | }
68 |
69 | public static CursorState operator +(CursorState a, CursorState b)
70 | {
71 | return new CursorState(a.Index + b.Index, a.Line + b.Line, a.Column + b.Column);
72 | }
73 |
74 | public static CursorState operator -(CursorState a, CursorState b)
75 | {
76 | return new CursorState(a.Index - b.Index, a.Line - b.Line, a.Column - b.Column);
77 | }
78 |
79 | public static implicit operator int(CursorState state) => state.Index;
80 | }
81 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/ImGui.cs:
--------------------------------------------------------------------------------
1 | using System.Numerics;
2 | using System.Text;
3 |
4 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
5 | {
6 | public static partial class ImGuiWChar
7 | {
8 | public const int StackallocLimit = 2048;
9 |
10 | public static unsafe Vector2 CalcTextSize(char* start, char* end)
11 | {
12 | int length = (int)(end - start);
13 |
14 | var byteCount = Encoding.UTF8.GetByteCount(new Span(start, length));
15 |
16 | byte* ptr;
17 | if (byteCount + 1 > StackallocLimit)
18 | {
19 | ptr = AllocT(byteCount + 1);
20 | }
21 | else
22 | {
23 | byte* pPtr = stackalloc byte[byteCount + 1];
24 | ptr = pPtr;
25 | }
26 | Encoding.UTF8.GetBytes(new Span(start, length), new Span(ptr, byteCount));
27 | ptr[byteCount] = 0;
28 |
29 | Vector2 result = ImGui.CalcTextSize(ptr);
30 |
31 | if (byteCount + 1 > StackallocLimit)
32 | {
33 | Free(ptr);
34 | }
35 |
36 | return result;
37 | }
38 |
39 | public static unsafe void AddText(ImDrawList* drawList, Vector2 pos, uint col, char* textBegin, char* textEnd)
40 | {
41 | int length = (int)(textEnd - textBegin);
42 |
43 | var byteCount = Encoding.UTF8.GetByteCount(new Span(textBegin, length));
44 |
45 | byte* ptr;
46 | if (byteCount + 1 > StackallocLimit)
47 | {
48 | ptr = AllocT(byteCount + 1);
49 | }
50 | else
51 | {
52 | byte* pPtr = stackalloc byte[byteCount + 1];
53 | ptr = pPtr;
54 | }
55 | Encoding.UTF8.GetBytes(new Span(textBegin, length), new Span(ptr, byteCount));
56 | ptr[byteCount] = 0;
57 |
58 | ImGui.AddText(drawList, pos, col, ptr);
59 |
60 | if (byteCount + 1 > StackallocLimit)
61 | {
62 | Free(ptr);
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/JumpTarget.cs:
--------------------------------------------------------------------------------
1 | using Hexa.NET.ImGui;
2 |
3 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
4 | {
5 | public struct JumpTarget
6 | {
7 | public int Index;
8 | public int Length;
9 | public int Line;
10 | public int Column;
11 | public ImGuiScrollFlags Flags;
12 |
13 | public JumpTarget(int index, int line, int column, ImGuiScrollFlags flags)
14 | {
15 | Index = index;
16 | Line = line;
17 | Column = column;
18 | Flags = flags;
19 | }
20 |
21 | public JumpTarget(int index, int length, int line, int column, ImGuiScrollFlags flags)
22 | {
23 | Index = index;
24 | Length = length;
25 | Line = line;
26 | Column = column;
27 |
28 | Flags = flags;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/LineIndexSpan.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | public readonly struct LineIndexSpan : IEquatable
4 | {
5 | public readonly int Start;
6 | public readonly int End;
7 |
8 | public LineIndexSpan(int start, int end)
9 | {
10 | Start = start;
11 | End = end;
12 | }
13 |
14 | public readonly int Length => End - Start;
15 |
16 | public readonly void Deconstruct(out int startSpanIndex, out int endSpanIndex)
17 | {
18 | startSpanIndex = Start;
19 | endSpanIndex = End;
20 | }
21 |
22 | public override bool Equals(object? obj)
23 | {
24 | return obj is LineIndexSpan span && Equals(span);
25 | }
26 |
27 | public bool Equals(LineIndexSpan other)
28 | {
29 | return Start == other.Start &&
30 | End == other.End;
31 | }
32 |
33 | public override int GetHashCode()
34 | {
35 | return HashCode.Combine(Start, End);
36 | }
37 |
38 | public static bool operator ==(LineIndexSpan left, LineIndexSpan right)
39 | {
40 | return left.Equals(right);
41 | }
42 |
43 | public static bool operator !=(LineIndexSpan left, LineIndexSpan right)
44 | {
45 | return !(left == right);
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/NewLineType.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | public enum NewLineType
4 | {
5 | CRLF,
6 | LF,
7 | CR,
8 | Mixed
9 | }
10 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/SearchResult.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.Utilities;
4 | using System;
5 |
6 | public unsafe struct SearchResult
7 | {
8 | public StdWString* String;
9 | public int Start;
10 | public int End;
11 | public int Line;
12 | public int Column;
13 |
14 | public SearchResult(StdWString* @string, int start, int len, int line, int column)
15 | {
16 | String = @string;
17 | Start = start;
18 | End = start + len;
19 | Line = line;
20 | Column = column;
21 | }
22 |
23 | public static SearchResult FromIndex(StdWString* @string, int index, int len, TextSource source)
24 | {
25 | int line = 0;
26 | int column = 0;
27 | for (; line < source.LineCount; line++)
28 | {
29 | var lineSpan = source.Lines[line];
30 | if (lineSpan.Start <= index && lineSpan.End >= index)
31 | {
32 | column = index - lineSpan.Start;
33 | break;
34 | }
35 | }
36 |
37 | return new(@string, index, len, line, column);
38 | }
39 |
40 | public readonly int Length => End - Start;
41 |
42 | public readonly char* Data => String->Data + Start;
43 |
44 | public readonly char* DataEnd => String->Data + End;
45 |
46 | public readonly ReadOnlySpan AsReadOnlySpan()
47 | {
48 | return new ReadOnlySpan(String->Data + Start, Length);
49 | }
50 |
51 | public readonly Span AsSpan()
52 | {
53 | return new Span(String->Data + Start, Length);
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/SidePanel.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | public abstract class SidePanel : IDisposable
4 | {
5 | private bool disposedValue;
6 |
7 | public abstract string Icon { get; }
8 |
9 | public abstract string Title { get; }
10 |
11 | public void Draw()
12 | {
13 | DrawContent();
14 | }
15 |
16 | public abstract void DrawContent();
17 |
18 | protected virtual void DisposeCore()
19 | {
20 | }
21 |
22 | protected virtual void Dispose(bool disposing)
23 | {
24 | if (!disposedValue)
25 | {
26 | DisposeCore();
27 | disposedValue = true;
28 | }
29 | }
30 |
31 | public void Dispose()
32 | {
33 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
34 | Dispose(disposing: true);
35 | GC.SuppressFinalize(this);
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/SyntaxHighlightDefaults.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.ImGui.Widgets.Extras.TextEditor.Highlight.CSharp;
4 |
5 | public static class SyntaxHighlightDefaults
6 | {
7 | static SyntaxHighlightDefaults()
8 | {
9 | CSharp = new CSharpSyntaxHighlight();
10 | }
11 |
12 | public static SyntaxHighlight Default { get; } = new("Default", "");
13 |
14 | public static SyntaxHighlight CSharp { get; }
15 | }
16 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/SyntaxHighlightDefinition.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | public class SyntaxHighlightDefinition(string name, string pattern, ColorRGBA color)
4 | {
5 | public string Name { get; set; } = name;
6 |
7 | public string Pattern { get; set; } = pattern;
8 |
9 | public ColorRGBA Color { get; set; } = color;
10 | }
11 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextDrawData.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | public class TextDrawData
7 | {
8 | public readonly List Spans = [];
9 | public readonly List FoldSpans = [];
10 | public readonly List LineSpanIndices = [];
11 |
12 | public IEnumerable GetVisibleSpans(float scroll, float lineHeight, float maxHeight)
13 | {
14 | int start = (int)MathF.Floor(scroll / lineHeight);
15 | int end = (int)MathF.Ceiling(maxHeight / lineHeight) + start;
16 | end = Math.Min(end, LineSpanIndices.Count);
17 |
18 | for (int i = start; i < end; i++)
19 | {
20 | var (startSpanIndex, endSpanIndex) = LineSpanIndices[i];
21 |
22 | for (int j = startSpanIndex; j <= endSpanIndex; j++)
23 | {
24 | yield return Spans[j];
25 | }
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextEditOp.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | public enum TextEditOp
4 | {
5 | Unknown,
6 | Insert,
7 | Erase,
8 | Replace,
9 | Cut,
10 | Paste,
11 | }
12 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextHighlightSpan.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.ImGui;
4 | using Hexa.NET.Utilities;
5 | using System.Numerics;
6 |
7 | public unsafe struct TextHighlightSpan
8 | {
9 | public StdWString* String;
10 | public Vector2 Origin;
11 | public int Start;
12 | public int End;
13 | public float Size;
14 | public uint Color;
15 | public bool HasColor;
16 | public bool Control;
17 |
18 | public TextHighlightSpan(StdWString* str, Vector2 origin, ColorRGBA color, int start, int length, bool control = false)
19 | {
20 | String = str;
21 | Origin = origin;
22 | Start = start;
23 | End = start + length;
24 | Color = ImGui.ColorConvertFloat4ToU32(color);
25 | HasColor = true;
26 | Control = control;
27 | }
28 |
29 | public TextHighlightSpan(StdWString* str, Vector2 origin, ColorRGBA color, bool hasColor, int start, int length)
30 | {
31 | String = str;
32 | Origin = origin;
33 | Start = start;
34 | End = start + length;
35 | Color = ImGui.ColorConvertFloat4ToU32(color);
36 | HasColor = hasColor;
37 | }
38 |
39 | public TextHighlightSpan(StdWString* str, Vector2 origin, int start, int length)
40 | {
41 | String = str;
42 | Origin = origin;
43 | Start = start;
44 | End = start + length;
45 | HasColor = false;
46 | }
47 |
48 | public readonly int Length => End - Start;
49 |
50 | public readonly char* Data => String->Data + Start;
51 |
52 | public readonly ReadOnlySpan AsReadOnlySpan()
53 | {
54 | return new ReadOnlySpan(String->Data + Start, Length);
55 | }
56 |
57 | public readonly Span AsSpan()
58 | {
59 | return new Span(String->Data + Start, Length);
60 | }
61 |
62 | public override readonly string ToString()
63 | {
64 | return $"[{Start}-{End}] {Color:X}, {new string(Data, 0, Length)}";
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextHighlightSpanStartComparer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
3 | {
4 | public class TextHighlightSpanStartComparer : IComparer
5 | {
6 | public static readonly TextHighlightSpanStartComparer Instance = new();
7 |
8 | public int Compare(TextHighlightSpan x, TextHighlightSpan y)
9 | {
10 | if (x.Start > y.Start)
11 | {
12 | return 1;
13 | }
14 | else if (x.Start < y.Start)
15 | {
16 | return -1;
17 | }
18 | else
19 | {
20 | return 0;
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextHistory.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.Utilities;
4 |
5 | public unsafe class TextHistory
6 | {
7 | private readonly TextHistoryEntry[] undoHistory;
8 | private readonly TextHistoryEntry[] redoHistory;
9 | private readonly TextSource source;
10 | private readonly int maxCount;
11 | private int undoHistoryCount;
12 | private int redoHistoryCount;
13 | private bool disposedValue;
14 |
15 | public TextHistory(TextSource source, int maxCount)
16 | {
17 | this.source = source;
18 | this.maxCount = maxCount;
19 |
20 | undoHistory = new TextHistoryEntry[maxCount];
21 | redoHistory = new TextHistoryEntry[maxCount];
22 | }
23 |
24 | public int UndoCount => undoHistoryCount;
25 |
26 | public int RedoCount => redoHistoryCount;
27 |
28 | public bool CanUndo => undoHistoryCount > 0;
29 |
30 | public bool CanRedo => redoHistoryCount > 0;
31 |
32 | public void Clear()
33 | {
34 | for (int i = 0; i < undoHistory.Length; i++)
35 | {
36 | undoHistory[i].Release();
37 | }
38 | undoHistoryCount = 0;
39 | for (int i = 0; i < redoHistory.Length; i++)
40 | {
41 | redoHistory[i].Release();
42 | }
43 | redoHistoryCount = 0;
44 | }
45 |
46 | public void UndoPush()
47 | {
48 | UndoPushInternal();
49 | for (int i = 0; i < redoHistoryCount; i++)
50 | {
51 | var index = maxCount - redoHistoryCount;
52 | redoHistory[index].Release();
53 | }
54 | redoHistoryCount = 0;
55 | }
56 |
57 | private void UndoPushInternal()
58 | {
59 | var last = undoHistory[^1];
60 |
61 | // Release the last entry if it contains data
62 | last.Release();
63 |
64 | // Shift entries in undoHistory to the right
65 | for (int i = undoHistory.Length - 1; i > 0; i--)
66 | {
67 | undoHistory[i] = undoHistory[i - 1];
68 | }
69 |
70 | // Allocate a new StdString and clone the current text
71 | last.Data = AllocT();
72 | *last.Data = source.Text->Clone();
73 |
74 | // Place the new entry at the beginning of undoHistory
75 | undoHistory[0] = last;
76 | undoHistoryCount = Math.Min(undoHistoryCount + 1, maxCount);
77 | }
78 |
79 | private void RedoPushInternal(TextHistoryEntry entry)
80 | {
81 | var last = redoHistory[^1];
82 |
83 | // Release the last entry if it contains data
84 | last.Release();
85 |
86 | // Shift entries in redoHistory to the right
87 | for (int i = redoHistory.Length - 1; i > 0; i--)
88 | {
89 | redoHistory[i] = redoHistory[i - 1];
90 | }
91 |
92 | // Allocate a new StdString and clone the entry data
93 | last.Data = AllocT();
94 | *last.Data = entry.Data->Clone();
95 |
96 | // Place the new entry at the beginning of redoHistory
97 | redoHistory[0] = last;
98 | redoHistoryCount = Math.Min(redoHistoryCount + 1, maxCount);
99 | }
100 |
101 | public void Undo()
102 | {
103 | if (undoHistoryCount == 0)
104 | {
105 | return;
106 | }
107 |
108 | var first = undoHistory[0];
109 |
110 | // Shift entries in undoHistory to the left
111 | for (int i = 0; i < undoHistory.Length - 1; i++)
112 | {
113 | undoHistory[i] = undoHistory[i + 1];
114 | }
115 |
116 | RedoPushInternal(first);
117 |
118 | // Set the source text to the first entry's data
119 | source.SetText(first.Data);
120 |
121 | // Place the released entry at the end of undoHistory
122 | undoHistory[^1] = first;
123 | undoHistoryCount--;
124 | }
125 |
126 | public void Redo()
127 | {
128 | if (redoHistoryCount == 0)
129 | {
130 | return;
131 | }
132 |
133 | var first = redoHistory[0];
134 |
135 | // Shift entries in redoHistory to the left
136 | for (int i = 0; i < redoHistory.Length - 1; i++)
137 | {
138 | redoHistory[i] = redoHistory[i + 1];
139 | }
140 |
141 | UndoPushInternal();
142 |
143 | // Set the source text to the first entry's data
144 | source.SetText(first.Data);
145 |
146 | // Place the released entry at the end of redoHistory
147 | redoHistory[^1] = first;
148 | redoHistoryCount--;
149 | }
150 |
151 | public void Dispose()
152 | {
153 | if (disposedValue)
154 | {
155 | return;
156 | }
157 |
158 | Clear();
159 |
160 | disposedValue = true;
161 | }
162 | }
163 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextHistoryEntry.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.Utilities;
4 |
5 | public unsafe struct TextHistoryEntry
6 | {
7 | public StdWString* Data;
8 |
9 | public void Release()
10 | {
11 | if (Data != null)
12 | {
13 | Data->Release();
14 | Free(Data);
15 | Data = null;
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextSelection.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.Utilities;
4 | using System;
5 |
6 | public unsafe struct TextSelection
7 | {
8 | public StdWString* Text;
9 | public CursorState Start;
10 | public CursorState End;
11 |
12 | public TextSelection()
13 | {
14 | Start = CursorState.Invalid;
15 | End = CursorState.Invalid;
16 | }
17 |
18 | public TextSelection(StdWString* text, CursorState start, CursorState end)
19 | {
20 | Text = text;
21 | Start = start;
22 | End = end;
23 | }
24 |
25 | public static readonly TextSelection Invalid = new(null, CursorState.Invalid, CursorState.Invalid);
26 |
27 | public char* Data => Text->Data + Math.Min(Start.Index, End.Index);
28 |
29 | public readonly int Length => Math.Abs(End - Start);
30 |
31 | public bool IsValid()
32 | {
33 | var start = Start.Index;
34 | var end = End.Index;
35 | if (start > end)
36 | {
37 | (start, end) = (end, start);
38 | }
39 | return start >= 0 && Text != null && end <= Text->Size;
40 | }
41 |
42 | public readonly CursorState EffectiveStart => Start.Index <= End.Index ? Start : End;
43 |
44 | public readonly CursorState EffectiveEnd => Start.Index <= End.Index ? End : Start;
45 | }
46 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextSource.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.Utilities;
4 | using System.Globalization;
5 | using System.Numerics;
6 | using System.Text;
7 |
8 | public unsafe class TextSource
9 | {
10 | private StdWString* text;
11 | private NewLineType newLineType;
12 |
13 | public TextSource(string text)
14 | {
15 | this.text = AllocT();
16 | *this.text = new(text);
17 | IsBinary = !IsText(this.text);
18 | newLineType = GetNewLineType(this.text);
19 | LineCount = CountLines(this.text);
20 | }
21 |
22 | public StdWString* Text
23 | {
24 | get => text;
25 | }
26 |
27 | public Encoding Encoding { get; set; } = Encoding.UTF8;
28 |
29 | public NewLineType NewLineType
30 | {
31 | get => newLineType;
32 | set
33 | {
34 | newLineType = value;
35 | if (value == NewLineType.Mixed)
36 | {
37 | return;
38 | }
39 | ConvertNewLineType(text, value);
40 | LineCount = CountLines(text);
41 | }
42 | }
43 |
44 | public bool IsBinary { get; set; }
45 |
46 | public bool Changed { get; set; }
47 |
48 | public int LineCount { get; set; }
49 |
50 | public int CharacterCount { get; set; }
51 |
52 | public void SetText(StdWString* newText)
53 | {
54 | text->Resize(newText->Size);
55 | MemcpyT(newText->CStr(), text->CStr(), newText->Size);
56 | }
57 |
58 | public static bool IsText(StdWString* text)
59 | {
60 | for (int i = 0; i < text->Size; i++)
61 | {
62 | char c = (*text)[i];
63 | if (char.GetUnicodeCategory(c) == UnicodeCategory.Control)
64 | {
65 | return false;
66 | }
67 | }
68 |
69 | return true;
70 | }
71 |
72 | public static NewLineType GetNewLineType(StdWString* text)
73 | {
74 | if (text->Size == 0)
75 | {
76 | return NewLineType.CRLF;
77 | }
78 |
79 | bool crlf = false;
80 | bool lf = false;
81 | bool cr = false;
82 |
83 | bool foundFlag = false;
84 |
85 | for (int i = 0; i < text->Size; i++)
86 | {
87 | char c = (*text)[i];
88 | char c1 = (i + 1 < text->Size) ? (*text)[i + 1] : '\0';
89 |
90 | if (c == '\r' && c1 == '\n')
91 | {
92 | if (foundFlag && !crlf)
93 | {
94 | return NewLineType.Mixed;
95 | }
96 | crlf = true;
97 | foundFlag = true;
98 | i++;
99 | }
100 | else if (c == '\n')
101 | {
102 | if (foundFlag && !lf)
103 | {
104 | return NewLineType.Mixed;
105 | }
106 | lf = true;
107 | foundFlag = true;
108 | }
109 | else if (c == '\r')
110 | {
111 | if (foundFlag && !cr)
112 | {
113 | return NewLineType.Mixed;
114 | }
115 | cr = true;
116 | foundFlag = true;
117 | }
118 | }
119 |
120 | if (crlf)
121 | {
122 | return NewLineType.CRLF;
123 | }
124 | if (lf)
125 | {
126 | return NewLineType.LF;
127 | }
128 | if (cr)
129 | {
130 | return NewLineType.CR;
131 | }
132 |
133 | return NewLineType.Mixed;
134 | }
135 |
136 | public List Lines { get; } = [];
137 |
138 | public float MaxLineWidth { get; private set; }
139 |
140 | public Vector2 LayoutSize { get; private set; }
141 |
142 | public void Update(float lineHeight)
143 | {
144 | char* pText = text->Data;
145 | Lines.Clear();
146 | int lineStart = 0;
147 | float maxWidth = 0;
148 |
149 | for (int i = 0; i < text->Size; i++)
150 | {
151 | char c = pText[i];
152 |
153 | if (c == '\n' || c == '\r')
154 | {
155 | // for CRLF
156 | if (c == '\r' && i < text->Size - 1 && pText[i + 1] == '\n')
157 | {
158 | i++;
159 | }
160 |
161 | TextSpan span = new(text, lineStart, i - lineStart);
162 | span.Size = ImGuiWChar.CalcTextSize(pText + lineStart, pText + i).X;
163 | maxWidth = Math.Max(maxWidth, span.Size);
164 | Lines.Add(span);
165 | lineStart = i + 1;
166 | }
167 | }
168 |
169 | if (lineStart <= text->Size)
170 | {
171 | TextSpan span = new(text, lineStart, text->Size - lineStart);
172 | span.Size = ImGuiWChar.CalcTextSize(pText + lineStart, pText + text->Size).X;
173 | maxWidth = Math.Max(maxWidth, span.Size);
174 | Lines.Add(span);
175 | }
176 |
177 | MaxLineWidth = maxWidth;
178 | LineCount = Lines.Count;
179 | LayoutSize = new(maxWidth, Lines.Count * lineHeight);
180 | }
181 |
182 | public static void ConvertNewLineType(StdWString* text, NewLineType newLineType)
183 | {
184 | // don't care just return original, you can't convert it to mixed anyway.
185 | if (newLineType == NewLineType.Mixed)
186 | {
187 | return;
188 | }
189 |
190 | string separator = newLineType switch
191 | {
192 | NewLineType.CRLF => "\r\n",
193 | NewLineType.LF => "\n",
194 | NewLineType.CR => "\r",
195 | _ => throw new NotSupportedException(),
196 | };
197 | StringBuilder sb = new();
198 | for (int i = 0; i < text->Size; i++)
199 | {
200 | char c = (*text)[i];
201 | char c1 = (*text)[i + 1 == text->Size ? i : i + 1];
202 |
203 | if (c == '\r' && c1 == '\n' || c == '\n' || c == '\r')
204 | {
205 | sb.Append(separator);
206 | if (c1 == '\n')
207 | {
208 | i++;
209 | }
210 | continue;
211 | }
212 |
213 | sb.Append(c);
214 | }
215 | return;
216 | }
217 |
218 | public static int CountLines(StdWString* text)
219 | {
220 | int lineCount = 1;
221 |
222 | foreach (char c in *text)
223 | {
224 | if (c == '\n')
225 | {
226 | lineCount++;
227 | }
228 | }
229 |
230 | return lineCount;
231 | }
232 |
233 | public void Dispose()
234 | {
235 | text->Release();
236 | Free(text);
237 | text = null;
238 | }
239 | }
240 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextSpan.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
2 | {
3 | using Hexa.NET.Utilities;
4 | using System.Diagnostics;
5 |
6 | [DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
7 | public unsafe struct TextSpan : IEquatable
8 | {
9 | public StdWString* String;
10 | public int Start;
11 | public int End;
12 | public float Size;
13 |
14 | public TextSpan(StdWString* str, int start, int length)
15 | {
16 | String = str;
17 | Start = start;
18 | End = start + length;
19 | }
20 |
21 | public readonly int Length => End - Start;
22 |
23 | public readonly char* Data => String->Data + Start;
24 |
25 | public readonly char* DataEnd => String->Data + End;
26 |
27 | public readonly ReadOnlySpan AsReadOnlySpan()
28 | {
29 | return new ReadOnlySpan(String->Data + Start, Length);
30 | }
31 |
32 | public readonly Span AsSpan()
33 | {
34 | return new Span(String->Data + Start, Length);
35 | }
36 |
37 | public override readonly bool Equals(object? obj)
38 | {
39 | return obj is TextSpan span && Equals(span);
40 | }
41 |
42 | public readonly bool Equals(TextSpan other)
43 | {
44 | return Start == other.Start &&
45 | End == other.End;
46 | }
47 |
48 | public override readonly int GetHashCode()
49 | {
50 | return HashCode.Combine(Start, End);
51 | }
52 |
53 | public static bool operator ==(TextSpan left, TextSpan right)
54 | {
55 | return left.Equals(right);
56 | }
57 |
58 | public static bool operator !=(TextSpan left, TextSpan right)
59 | {
60 | return !(left == right);
61 | }
62 |
63 | private readonly string GetDebuggerDisplay()
64 | {
65 | return $"{Start} .. {End} {new string(Data, 0, Length)}";
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.Extras/TextEditor/TextSpanStartComparer.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Hexa.NET.ImGui.Widgets.Extras.TextEditor
3 | {
4 | public class TextSpanStartComparer : IComparer
5 | {
6 | public static readonly TextSpanStartComparer Instance = new();
7 |
8 | public int Compare(TextSpan x, TextSpan y)
9 | {
10 | if (x.Start > y.Start)
11 | {
12 | return 1;
13 | }
14 | else if (x.Start < y.Start)
15 | {
16 | return -1;
17 | }
18 | else
19 | {
20 | return 0;
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.10.35122.118
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hexa.NET.ImGui.Widgets", "Hexa.NET.ImGui.Widgets\Hexa.NET.ImGui.Widgets.csproj", "{18AA69A5-04E1-4E49-8CB8-378DF59CF980}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "TestApp\TestApp.csproj", "{088B7207-ED91-435C-8A35-9ED64A510F50}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hexa.NET.ImGui.Widgets.Extras", "Hexa.NET.ImGui.Widgets.Extras\Hexa.NET.ImGui.Widgets.Extras.csproj", "{A9F458A4-F994-43C3-944E-1E2E635F3A7D}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PerformanceTests", "PerformanceTests\PerformanceTests.csproj", "{B0B1BFA5-9CB4-4811-9167-A8B7566EAF41}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextEditor", "TextEditor\TextEditor.csproj", "{442A0F34-84DA-4696-BD2D-35E7CAE91B5D}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {18AA69A5-04E1-4E49-8CB8-378DF59CF980}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {18AA69A5-04E1-4E49-8CB8-378DF59CF980}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {18AA69A5-04E1-4E49-8CB8-378DF59CF980}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {18AA69A5-04E1-4E49-8CB8-378DF59CF980}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {088B7207-ED91-435C-8A35-9ED64A510F50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {088B7207-ED91-435C-8A35-9ED64A510F50}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {088B7207-ED91-435C-8A35-9ED64A510F50}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {088B7207-ED91-435C-8A35-9ED64A510F50}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {A9F458A4-F994-43C3-944E-1E2E635F3A7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {A9F458A4-F994-43C3-944E-1E2E635F3A7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {A9F458A4-F994-43C3-944E-1E2E635F3A7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {A9F458A4-F994-43C3-944E-1E2E635F3A7D}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {B0B1BFA5-9CB4-4811-9167-A8B7566EAF41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {B0B1BFA5-9CB4-4811-9167-A8B7566EAF41}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {B0B1BFA5-9CB4-4811-9167-A8B7566EAF41}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {B0B1BFA5-9CB4-4811-9167-A8B7566EAF41}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {442A0F34-84DA-4696-BD2D-35E7CAE91B5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {442A0F34-84DA-4696-BD2D-35E7CAE91B5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {442A0F34-84DA-4696-BD2D-35E7CAE91B5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {442A0F34-84DA-4696-BD2D-35E7CAE91B5D}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(ExtensibilityGlobals) = postSolution
47 | SolutionGuid = {52FCE378-A076-497C-A15D-CA3BE20AD9FD}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/Hexa.NET.ImGui.Widgets/ComboHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Hexa.NET.ImGui.Widgets
2 | {
3 | using Hexa.NET.ImGui;
4 | using System;
5 | using System.Linq;
6 | using System.Diagnostics.CodeAnalysis;
7 |
8 | ///
9 | /// A helper class for working with ImGui combo boxes to select enum values of a specified enum type.
10 | ///
11 | /// The enum type.
12 | public static class ComboEnumHelper where T : struct, Enum
13 | {
14 | #if NET5_0_OR_GREATER
15 | private static readonly T[] values = Enum.GetValues();
16 | private static readonly string[] names = Enum.GetNames();
17 | #else
18 | private static readonly T[] values = (T[])Enum.GetValues(typeof(T));
19 | private static readonly string[] names = Enum.GetNames(typeof(T));
20 | #endif
21 |
22 | ///
23 | /// Displays a combo box to select an enum value.
24 | ///
25 | /// The label for the combo box.
26 | /// The currently selected enum value (modified by user interaction).
27 | /// true if the user selects a new value, false otherwise.
28 | public static bool Combo(string label, ref T value)
29 | {
30 | int index = Array.IndexOf(values, value);
31 | if (ImGui.Combo(label, ref index, names, names.Length))
32 | {
33 | value = values[index];
34 | return true;
35 | }
36 | return false;
37 | }
38 |
39 | ///
40 | /// Displays the text representation of an enum value.
41 | ///
42 | /// The enum value to display.
43 | public static void Text(T value)
44 | {
45 | int index = Array.IndexOf(values, value);
46 | ImGui.Text(names[index]);
47 | }
48 |
49 | public static string GetName(T value)
50 | {
51 | int index = Array.IndexOf(values, value);
52 | return names[index];
53 | }
54 | }
55 |
56 | ///
57 | /// A helper class for working with ImGui combo boxes to select enum values of various enum types.
58 | ///
59 | public static class ComboEnumHelper
60 | {
61 | private static readonly Dictionary values = new();
62 | private static readonly Dictionary names = new();
63 | #if NET7_0_OR_GREATER
64 |
65 | [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "All members are included by [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)].")]
66 | #endif
67 | private static void Get(
68 | #if NET7_0_OR_GREATER
69 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
70 | #endif
71 | Type type, out object[] values, out string[] names)
72 | {
73 | if (ComboEnumHelper.values.TryGetValue(type, out var objects))
74 | {
75 | values = objects;
76 | names = ComboEnumHelper.names[type];
77 | return;
78 | }
79 |
80 | values = Enum.GetValues(type).Cast