├── .github └── workflows │ └── bump-version.yml ├── .gitignore ├── LICENSE ├── MonoGame-Textbox ├── Cursor.cs ├── Extensions.cs ├── KeyboardInput.cs ├── MonoGame-Textbox.csproj ├── MonoGame-Textbox.nuspec ├── MonoGame-Textbox.sln ├── MonoGame-Textbox.sln.DotSettings ├── Properties │ └── AssemblyInfo.cs ├── Text.cs ├── TextBox.cs ├── TextRenderer.cs └── packages.config ├── README.md ├── Textbox-Test.png ├── Textbox-Test ├── Content │ ├── Arsenal.spritefont │ ├── Content.mgcb │ └── bin │ │ └── Windows │ │ └── Arsenal.xnb ├── Game1.cs ├── Icon.ico ├── Primitives2D.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Textbox-Test.csproj ├── app.config ├── app.manifest └── packages.config ├── icon.png ├── nuget_pack.bat └── nuget_push.bat /.github/workflows/bump-version.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Bump version 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@master 13 | with: 14 | fetch-depth: '0' 15 | - name: Bump version and push tag 16 | uses: anothrNick/github-tag-action@master 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | RELEASE_BRANCHES: master 20 | DEFAULT_BUMP: patch 21 | WITH_V: false 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | 24 | # Visual Studio 2015 cache/options directory 25 | .vs/ 26 | # Uncomment if you have tasks that create the project's static files in wwwroot 27 | #wwwroot/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | # DNX 43 | project.lock.json 44 | artifacts/ 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | # NuGet v3's project.json files produces more ignoreable files 154 | *.nuget.props 155 | *.nuget.targets 156 | 157 | # Microsoft Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Microsoft Azure Emulator 162 | ecf/ 163 | rcf/ 164 | 165 | # Microsoft Azure ApplicationInsights config file 166 | ApplicationInsights.config 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | 172 | # Visual Studio cache files 173 | # files ending in .cache can be ignored 174 | *.[Cc]ache 175 | # but keep track of directories ending in .cache 176 | !*.[Cc]ache/ 177 | 178 | # Others 179 | ClientBin/ 180 | ~$* 181 | *~ 182 | *.dbmdl 183 | *.dbproj.schemaview 184 | *.pfx 185 | *.publishsettings 186 | node_modules/ 187 | orleans.codegen.cs 188 | 189 | # RIA/Silverlight projects 190 | Generated_Code/ 191 | 192 | # Backup & report files from converting an old project file 193 | # to a newer Visual Studio version. Backup files are not needed, 194 | # because we have git ;-) 195 | _UpgradeReport_Files/ 196 | Backup*/ 197 | UpgradeLog*.XML 198 | UpgradeLog*.htm 199 | 200 | # SQL Server files 201 | *.mdf 202 | *.ldf 203 | 204 | # Business Intelligence projects 205 | *.rdl.data 206 | *.bim.layout 207 | *.bim_*.settings 208 | 209 | # Microsoft Fakes 210 | FakesAssemblies/ 211 | 212 | # GhostDoc plugin setting file 213 | *.GhostDoc.xml 214 | 215 | # Node.js Tools for Visual Studio 216 | .ntvs_analysis.dat 217 | 218 | # Visual Studio 6 build log 219 | *.plg 220 | 221 | # Visual Studio 6 workspace options file 222 | *.opt 223 | 224 | # Visual Studio LightSwitch build output 225 | **/*.HTMLClient/GeneratedArtifacts 226 | **/*.DesktopClient/GeneratedArtifacts 227 | **/*.DesktopClient/ModelManifest.xml 228 | **/*.Server/GeneratedArtifacts 229 | **/*.Server/ModelManifest.xml 230 | _Pvt_Extensions 231 | 232 | # Paket dependency manager 233 | .paket/paket.exe 234 | 235 | # FAKE - F# Make 236 | .fake/ 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /MonoGame-Textbox/Cursor.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using JetBrains.Annotations; 29 | using Microsoft.Xna.Framework; 30 | using Microsoft.Xna.Framework.Graphics; 31 | 32 | namespace MonoGame_Textbox 33 | { 34 | [PublicAPI] 35 | public class Cursor 36 | { 37 | public Color Color { get; set; } 38 | public Color Selection { get; set; } 39 | public Rectangle Icon { get; set; } 40 | 41 | public bool Active { get; set; } 42 | 43 | private bool visible; 44 | private readonly int ticksPerBlink; 45 | private int ticks; 46 | 47 | /// 48 | /// The current location of the cursor in the array 49 | /// 50 | public int TextCursor 51 | { 52 | get { return textCursor; } 53 | set { textCursor = value.Clamp(0, textBox.Text.Length); } 54 | } 55 | 56 | /// 57 | /// All characters between SelectedChar and the TextCursor are selected 58 | /// when SelectedChar != null. Cannot be the same as the TextCursor value. 59 | /// 60 | public int? SelectedChar 61 | { 62 | get { return selectedChar; } 63 | set 64 | { 65 | if (value.HasValue) 66 | { 67 | if (value.Value != TextCursor) 68 | { 69 | selectedChar = (short) (value.Value.Clamp(0, textBox.Text.Length)); 70 | } 71 | } 72 | else 73 | { 74 | selectedChar = null; 75 | } 76 | } 77 | } 78 | 79 | private readonly TextBox textBox; 80 | 81 | private int textCursor; 82 | private int? selectedChar; 83 | 84 | public Cursor(TextBox textBox, Color color, Color selection, Rectangle icon, int ticksPerBlink) 85 | { 86 | this.textBox = textBox; 87 | Color = color; 88 | Selection = selection; 89 | Icon = icon; 90 | Active = true; 91 | visible = false; 92 | this.ticksPerBlink = ticksPerBlink; 93 | ticks = 0; 94 | } 95 | 96 | public void Update() 97 | { 98 | ticks++; 99 | 100 | if (ticks <= ticksPerBlink) 101 | { 102 | return; 103 | } 104 | 105 | visible = !visible; 106 | ticks = 0; 107 | } 108 | 109 | public void Draw(SpriteBatch spriteBatch) 110 | { 111 | // Top left corner of the text area. 112 | int x = textBox.Renderer.Area.X; 113 | int y = textBox.Renderer.Area.Y; 114 | 115 | Point cp = GetPosition(x, y, TextCursor); 116 | if (selectedChar.HasValue) 117 | { 118 | Point sc = GetPosition(x, y, selectedChar.Value); 119 | if (sc.X > cp.X) 120 | { 121 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), 122 | new Rectangle(cp.X, cp.Y, sc.X - cp.X, textBox.Renderer.Font.LineSpacing), Icon, Selection); 123 | } 124 | else 125 | { 126 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), 127 | new Rectangle(sc.X, sc.Y, cp.X - sc.X, textBox.Renderer.Font.LineSpacing), Icon, Selection); 128 | } 129 | } 130 | 131 | if (!visible) 132 | { 133 | return; 134 | } 135 | 136 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), 137 | new Rectangle(cp.X, cp.Y, Icon.Width, textBox.Renderer.Font.LineSpacing), Icon, Color); 138 | } 139 | 140 | private Point GetPosition(int x, int y, int pos) 141 | { 142 | if (pos > 0) 143 | { 144 | if (textBox.Text.Characters[pos - 1] == '\n' 145 | || textBox.Text.Characters[pos - 1] == '\r') 146 | { 147 | // Beginning of next line. 148 | y += textBox.Renderer.Y[pos - 1] + textBox.Renderer.Font.LineSpacing; 149 | } 150 | else if (pos == textBox.Text.Length) 151 | { 152 | // After last character. 153 | x += textBox.Renderer.X[pos - 1] + textBox.Renderer.Width[pos - 1]; 154 | y += textBox.Renderer.Y[pos - 1]; 155 | } 156 | else 157 | { 158 | // Beginning of current character. 159 | x += textBox.Renderer.X[pos]; 160 | y += textBox.Renderer.Y[pos]; 161 | } 162 | } 163 | return new Point(x, y); 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /MonoGame-Textbox/Extensions.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using Microsoft.Xna.Framework; 29 | using Microsoft.Xna.Framework.Graphics; 30 | 31 | namespace MonoGame_Textbox 32 | { 33 | public static class Extensions 34 | { 35 | private static Texture2D pixel; 36 | 37 | public static Texture2D GetWhitePixel(this SpriteBatch spriteBatch) 38 | { 39 | if (pixel == null) 40 | { 41 | pixel = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, 42 | SurfaceFormat.Color); 43 | pixel.SetData(new[] {Color.White}); 44 | } 45 | return pixel; 46 | } 47 | 48 | public static int Clamp(this int value, int min, int max) 49 | { 50 | if (value > max) 51 | { 52 | return max; 53 | } 54 | if (value < min) 55 | { 56 | return min; 57 | } 58 | return value; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /MonoGame-Textbox/KeyboardInput.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using System; 29 | using System.Linq; 30 | using Microsoft.Xna.Framework; 31 | using Microsoft.Xna.Framework.Input; 32 | 33 | namespace MonoGame_Textbox 34 | { 35 | /// 36 | /// All printable characters are returned using the CharPressed event and captured using the Game.Window.TextInput 37 | /// event exposed by MonoGame. 38 | /// Those automatically honor keyboard-layout and repeat-frequency of the keyboard for all platforms. 39 | /// The flag filterSpecialCharactersFromCharPressed you may specify when calling Initialize tells the class to filter 40 | /// those characters exposed in the SpecialCharacters char[] or not. 41 | /// When running on a OpenGl-system those characters are not captured by the system. So the default for filtering them 42 | /// out is for the sake of compatibility. 43 | /// The repetition of the special characters as well as the arrow keys, etc, is handled by the class itself using the 44 | /// values you pass it when calling Initialize. 45 | /// The repetition is not done by sending the characters through the CharPressed event (since they may be not even 46 | /// characters and some of them are omitted by the OpenGl platform), but through the KeyPressed event and the keys are 47 | /// captured by getting the KeyboardState from MonoGame in the Update-method. 48 | /// So if you want to capture those, use that one. 49 | /// The KeyDown and KeyUp event are standard events being getting the KeyboardState from MonoGame in the Update-method. 50 | /// 51 | public static class KeyboardInput 52 | { 53 | public class CharacterEventArgs : EventArgs 54 | { 55 | public char Character { get; private set; } 56 | 57 | public CharacterEventArgs(char character) 58 | { 59 | Character = character; 60 | } 61 | } 62 | 63 | public class KeyEventArgs : EventArgs 64 | { 65 | public Keys KeyCode { get; private set; } 66 | 67 | public KeyEventArgs(Keys keyCode) 68 | { 69 | KeyCode = keyCode; 70 | } 71 | } 72 | 73 | public delegate void CharEnteredHandler(object sender, CharacterEventArgs e, KeyboardState ks); 74 | 75 | public delegate void KeyEventHandler(object sender, KeyEventArgs e, KeyboardState ks); 76 | 77 | public static readonly char[] SPECIAL_CHARACTERS = {'\a', '\b', '\n', '\r', '\f', '\t', '\v'}; 78 | 79 | private static Game game; 80 | 81 | public static event CharEnteredHandler CharPressed; 82 | public static event KeyEventHandler KeyPressed; 83 | public static event KeyEventHandler KeyDown; 84 | public static event KeyEventHandler KeyUp; 85 | 86 | private static KeyboardState prevKeyState; 87 | 88 | private static Keys? repChar; 89 | private static DateTime downSince = DateTime.Now; 90 | private static float timeUntilRepInMillis; 91 | private static int repsPerSec; 92 | private static DateTime lastRep = DateTime.Now; 93 | private static bool filterSpecialCharacters; 94 | 95 | public static void Initialize(Game g, float timeUntilRepInMilliseconds, int repsPerSecond, 96 | bool filterSpecialCharactersFromCharPressed = true) 97 | { 98 | game = g; 99 | timeUntilRepInMillis = timeUntilRepInMilliseconds; 100 | repsPerSec = repsPerSecond; 101 | filterSpecialCharacters = filterSpecialCharactersFromCharPressed; 102 | game.Window.TextInput += TextEntered; 103 | } 104 | 105 | public static bool ShiftDown 106 | { 107 | get 108 | { 109 | KeyboardState state = Keyboard.GetState(); 110 | return state.IsKeyDown(Keys.LeftShift) || state.IsKeyDown(Keys.RightShift); 111 | } 112 | } 113 | 114 | public static bool CtrlDown 115 | { 116 | get 117 | { 118 | KeyboardState state = Keyboard.GetState(); 119 | return state.IsKeyDown(Keys.LeftControl) || state.IsKeyDown(Keys.RightControl); 120 | } 121 | } 122 | 123 | public static bool AltDown 124 | { 125 | get 126 | { 127 | KeyboardState state = Keyboard.GetState(); 128 | return state.IsKeyDown(Keys.LeftAlt) || state.IsKeyDown(Keys.RightAlt); 129 | } 130 | } 131 | 132 | private static void TextEntered(object sender, TextInputEventArgs e) 133 | { 134 | if (CharPressed != null) 135 | { 136 | if (!filterSpecialCharacters || !SPECIAL_CHARACTERS.Contains(e.Character)) 137 | { 138 | CharPressed(null, new CharacterEventArgs(e.Character), Keyboard.GetState()); 139 | } 140 | } 141 | } 142 | 143 | public static void Update() 144 | { 145 | KeyboardState keyState = Keyboard.GetState(); 146 | 147 | foreach (Keys key in (Keys[]) Enum.GetValues(typeof(Keys))) 148 | { 149 | if (JustPressed(keyState, key)) 150 | { 151 | KeyDown?.Invoke(null, new KeyEventArgs(key), keyState); 152 | if (KeyPressed != null) 153 | { 154 | downSince = DateTime.Now; 155 | repChar = key; 156 | KeyPressed(null, new KeyEventArgs(key), keyState); 157 | } 158 | } 159 | else if (JustReleased(keyState, key)) 160 | { 161 | if (KeyUp != null) 162 | { 163 | if (repChar == key) 164 | { 165 | repChar = null; 166 | } 167 | KeyUp(null, new KeyEventArgs(key), keyState); 168 | } 169 | } 170 | 171 | if (repChar != null && repChar == key && keyState.IsKeyDown(key)) 172 | { 173 | DateTime now = DateTime.Now; 174 | TimeSpan downFor = now.Subtract(downSince); 175 | if (downFor.CompareTo(TimeSpan.FromMilliseconds(timeUntilRepInMillis)) > 0) 176 | { 177 | // Should repeat since the wait time is over now. 178 | TimeSpan repeatSince = now.Subtract(lastRep); 179 | if (repeatSince.CompareTo(TimeSpan.FromMilliseconds(1000f / repsPerSec)) > 0) 180 | { 181 | // Time for another key-stroke. 182 | if (KeyPressed != null) 183 | { 184 | lastRep = now; 185 | KeyPressed(null, new KeyEventArgs(key), keyState); 186 | } 187 | } 188 | } 189 | } 190 | } 191 | 192 | prevKeyState = keyState; 193 | } 194 | 195 | private static bool JustPressed(KeyboardState keyState, Keys key) 196 | { 197 | return keyState.IsKeyDown(key) && prevKeyState.IsKeyUp(key); 198 | } 199 | 200 | private static bool JustReleased(KeyboardState keyState, Keys key) 201 | { 202 | return prevKeyState.IsKeyDown(key) && keyState.IsKeyUp(key); 203 | } 204 | 205 | public static void Dispose() 206 | { 207 | CharPressed = null; 208 | KeyDown = null; 209 | KeyPressed = null; 210 | KeyUp = null; 211 | } 212 | } 213 | } -------------------------------------------------------------------------------- /MonoGame-Textbox/MonoGame-Textbox.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10.0 6 | Debug 7 | AnyCPU 8 | {60845196-31B8-487A-8F25-83EAC47DA83D} 9 | Library 10 | Properties 11 | MonoGame_Textbox 12 | MonoGame-Textbox 13 | 14 | en-US 15 | 512 16 | 17 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 18 | Profile111 19 | v4.5 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll 41 | True 42 | 43 | 44 | packages\MonoGame.Framework.Portable.3.6.0.1625\lib\portable-net45+win8+wpa81\MonoGame.Framework.dll 45 | True 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /MonoGame-Textbox/MonoGame-Textbox.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $id$ 5 | $version$ 6 | $title$ 7 | Unterrainer Informatik OG Team 8 | Public Domain 9 | http://unlicense.org 10 | https://github.com/UnterrainerInformatik/Monogame-Textbox/raw/master/icon.png 11 | https://github.com/UnterrainerInformatik/MonoGame-Textbox 12 | false 13 | true 14 | An editable, multi-language, event-driven textbox for MonoGame that supports selection and cut/copy/paste. 15 | Just import this nuget package. It is a PCL so you should be able to use it in any MG project. 16 | Look at the git-repo for the usage since the repo there contains a test-project. 17 | 18 | An editable, multi-language, event-driven textbox for MonoGame that supports selection and cut/copy/paste. 19 | 20 | 21 | Copyright 2017 22 | en-US 23 | MonoGame textbox text input chat event driven cursor copy cut paste 24 | 25 | -------------------------------------------------------------------------------- /MonoGame-Textbox/MonoGame-Textbox.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame-Textbox", "MonoGame-Textbox.csproj", "{60845196-31B8-487A-8F25-83EAC47DA83D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Textbox-Test", "..\Textbox-Test\Textbox-Test.csproj", "{D42297D9-5678-4449-BBFF-0424B35F6762}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|x86 = Debug|x86 14 | Release|Any CPU = Release|Any CPU 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Debug|x86.ActiveCfg = Debug|Any CPU 21 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Debug|x86.Build.0 = Debug|Any CPU 22 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Release|x86.ActiveCfg = Release|Any CPU 25 | {60845196-31B8-487A-8F25-83EAC47DA83D}.Release|x86.Build.0 = Release|Any CPU 26 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Debug|Any CPU.ActiveCfg = Debug|x86 27 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Debug|Any CPU.Build.0 = Debug|x86 28 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Debug|x86.ActiveCfg = Debug|x86 29 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Debug|x86.Build.0 = Debug|x86 30 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Release|Any CPU.ActiveCfg = Release|x86 31 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Release|Any CPU.Build.0 = Release|x86 32 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Release|x86.ActiveCfg = Release|x86 33 | {D42297D9-5678-4449-BBFF-0424B35F6762}.Release|x86.Build.0 = Release|x86 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /MonoGame-Textbox/MonoGame-Textbox.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | *************************************************************************** 3 | This is free and unencumbered software released into the public domain. 4 | 5 | Anyone is free to copy, modify, publish, use, compile, sell, or 6 | distribute this software, either in source code form or as a compiled 7 | binary, for any purpose, commercial or non-commercial, and by any 8 | means. 9 | 10 | In jurisdictions that recognize copyright laws, the author or authors 11 | of this software dedicate any and all copyright interest in the 12 | software to the public domain. We make this dedication for the benefit 13 | of the public at large and to the detriment of our heirs and 14 | successors. We intend this dedication to be an overt act of 15 | relinquishment in perpetuity of all present and future rights to this 16 | software under copyright law. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | For more information, please refer to <http://unlicense.org> 27 | *************************************************************************** -------------------------------------------------------------------------------- /MonoGame-Textbox/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("MonoGame-Textbox")] 9 | [assembly: AssemblyDescription("An editable, multi-language, event-driven textbox for MonoGame that supports selection and cut/copy/paste.")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Unterrainer Informatik OG")] 12 | [assembly: AssemblyProduct("MonoGame-Textbox")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("60845196-31b8-487a-8f25-83eac47da83d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.1.0")] 36 | [assembly: AssemblyFileVersion("1.0.1.0")] 37 | -------------------------------------------------------------------------------- /MonoGame-Textbox/Text.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using System; 29 | 30 | namespace MonoGame_Textbox 31 | { 32 | /// 33 | /// Data structure responsible for keeping track of the characters. 34 | /// 35 | public class Text 36 | { 37 | public string String 38 | { 39 | get { return new string(_char).Substring(0, Length); } 40 | set { ResetText(value); } 41 | } 42 | 43 | public char[] Characters 44 | { 45 | get { return _char; } 46 | set { ResetText(new string(value)); } 47 | } 48 | 49 | private int length; 50 | 51 | public int Length 52 | { 53 | get { return length; } 54 | private set 55 | { 56 | length = value; 57 | if (length < MaxLength) 58 | { 59 | _char[length] = '\0'; 60 | } 61 | IsDirty = true; 62 | } 63 | } 64 | 65 | public int MaxLength { get; } 66 | 67 | public bool IsDirty { get; set; } 68 | 69 | private readonly char[] _char; 70 | 71 | public Text(int maxLength) 72 | { 73 | MaxLength = maxLength; 74 | 75 | _char = new char[MaxLength]; 76 | 77 | IsDirty = true; 78 | } 79 | 80 | public void InsertCharacter(int location, char character) 81 | { 82 | ValidateEditRange(location, location); 83 | ValidateLenght(location, location, 1); 84 | 85 | // Validation. 86 | if (!(Length < MaxLength)) 87 | { 88 | return; 89 | } 90 | 91 | // Shift everything right once then insert the character into the gap. 92 | Array.Copy( 93 | _char, location, 94 | _char, location + 1, 95 | Length - location); 96 | 97 | _char[location] = character; 98 | Length++; 99 | IsDirty = true; 100 | } 101 | 102 | public void Replace(int start, int end, string replacement) 103 | { 104 | ValidateEditRange(start, end); 105 | ValidateLenght(start, end, replacement.Length); 106 | 107 | RemoveCharacters(start, end); 108 | foreach (char character in replacement) 109 | { 110 | InsertCharacter(start, character); 111 | start++; 112 | } 113 | 114 | IsDirty = true; 115 | } 116 | 117 | public void RemoveCharacters(int start, int end) 118 | { 119 | ValidateEditRange(start, end); 120 | 121 | Array.Copy(_char, end, _char, start, Length - end); 122 | Length -= end - start; 123 | IsDirty = true; 124 | } 125 | 126 | private void ResetText(string value) 127 | { 128 | Length = 0; 129 | ValidateLenght(0, 0, value.Length); 130 | 131 | int x = value.IndexOf('\0'); 132 | if (x != -1) 133 | { 134 | value = value.Substring(0, x); 135 | } 136 | 137 | Length = value.Length; 138 | Array.Clear(_char, 0, _char.Length); 139 | value.ToCharArray().CopyTo(_char, 0); 140 | IsDirty = true; 141 | } 142 | 143 | // ReSharper disable UnusedParameter.Local 144 | private void ValidateEditRange(int start, int end) 145 | { 146 | if (end > Length || start < 0 || start > end) 147 | { 148 | throw new ArgumentException("Invalid character range"); 149 | } 150 | } 151 | 152 | private void ValidateLenght(int start, int end, int added) 153 | { 154 | if (Length - (end - start) + added > MaxLength) 155 | { 156 | throw new ArgumentException("Character limit of " + MaxLength + " exceeded."); 157 | } 158 | } 159 | 160 | // ReSharper restore UnusedParameter.Local 161 | } 162 | } -------------------------------------------------------------------------------- /MonoGame-Textbox/TextBox.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using System; 29 | using JetBrains.Annotations; 30 | using Microsoft.Xna.Framework; 31 | using Microsoft.Xna.Framework.Graphics; 32 | using Microsoft.Xna.Framework.Input; 33 | 34 | namespace MonoGame_Textbox 35 | { 36 | [PublicAPI] 37 | public class TextBox 38 | { 39 | public GraphicsDevice GraphicsDevice { get; set; } 40 | 41 | public Rectangle Area 42 | { 43 | get { return Renderer.Area; } 44 | set { Renderer.Area = value; } 45 | } 46 | 47 | public readonly Text Text; 48 | public readonly TextRenderer Renderer; 49 | public readonly Cursor Cursor; 50 | 51 | public event EventHandler EnterDown; 52 | 53 | private string clipboard; 54 | 55 | public bool Active { get; set; } 56 | 57 | public TextBox(Rectangle area, int maxCharacters, string text, GraphicsDevice graphicsDevice, 58 | SpriteFont spriteFont, 59 | Color cursorColor, Color selectionColor, int ticksPerToggle) 60 | { 61 | GraphicsDevice = graphicsDevice; 62 | 63 | Text = new Text(maxCharacters) 64 | { 65 | String = text 66 | }; 67 | 68 | Renderer = new TextRenderer(this) 69 | { 70 | Area = area, 71 | Font = spriteFont, 72 | Color = Color.Black 73 | }; 74 | 75 | Cursor = new Cursor(this, cursorColor, selectionColor, new Rectangle(0, 0, 1, 1), ticksPerToggle); 76 | 77 | KeyboardInput.CharPressed += CharacterTyped; 78 | KeyboardInput.KeyPressed += KeyPressed; 79 | } 80 | 81 | public void Dispose() 82 | { 83 | KeyboardInput.Dispose(); 84 | } 85 | 86 | public void Clear() 87 | { 88 | Text.RemoveCharacters(0, Text.Length); 89 | Cursor.TextCursor = 0; 90 | Cursor.SelectedChar = null; 91 | } 92 | 93 | private void KeyPressed(object sender, KeyboardInput.KeyEventArgs e, KeyboardState ks) 94 | { 95 | if (Active) 96 | { 97 | int oldPos = Cursor.TextCursor; 98 | switch (e.KeyCode) 99 | { 100 | case Keys.Enter: 101 | EnterDown?.Invoke(this, e); 102 | break; 103 | case Keys.Left: 104 | if (KeyboardInput.CtrlDown) 105 | { 106 | Cursor.TextCursor = IndexOfLastCharBeforeWhitespace(Cursor.TextCursor, Text.Characters); 107 | } 108 | else 109 | { 110 | Cursor.TextCursor--; 111 | } 112 | ShiftMod(oldPos); 113 | break; 114 | case Keys.Right: 115 | if (KeyboardInput.CtrlDown) 116 | { 117 | Cursor.TextCursor = IndexOfNextCharAfterWhitespace(Cursor.TextCursor, Text.Characters); 118 | } 119 | else 120 | { 121 | Cursor.TextCursor++; 122 | } 123 | ShiftMod(oldPos); 124 | break; 125 | case Keys.Home: 126 | Cursor.TextCursor = 0; 127 | ShiftMod(oldPos); 128 | break; 129 | case Keys.End: 130 | Cursor.TextCursor = Text.Length; 131 | ShiftMod(oldPos); 132 | break; 133 | case Keys.Delete: 134 | if (DelSelection() == null && Cursor.TextCursor < Text.Length) 135 | { 136 | Text.RemoveCharacters(Cursor.TextCursor, Cursor.TextCursor + 1); 137 | } 138 | break; 139 | case Keys.Back: 140 | if (DelSelection() == null && Cursor.TextCursor > 0) 141 | { 142 | Text.RemoveCharacters(Cursor.TextCursor - 1, Cursor.TextCursor); 143 | Cursor.TextCursor--; 144 | } 145 | break; 146 | case Keys.A: 147 | if (KeyboardInput.CtrlDown) 148 | { 149 | if (Text.Length > 0) 150 | { 151 | Cursor.SelectedChar = 0; 152 | Cursor.TextCursor = Text.Length; 153 | } 154 | } 155 | break; 156 | case Keys.C: 157 | if (KeyboardInput.CtrlDown) 158 | { 159 | clipboard = DelSelection(true); 160 | } 161 | break; 162 | case Keys.X: 163 | if (KeyboardInput.CtrlDown) 164 | { 165 | if (Cursor.SelectedChar.HasValue) 166 | { 167 | clipboard = DelSelection(); 168 | } 169 | } 170 | break; 171 | case Keys.V: 172 | if (KeyboardInput.CtrlDown) 173 | { 174 | if (clipboard != null) 175 | { 176 | DelSelection(); 177 | foreach (char c in clipboard) 178 | { 179 | if (Text.Length < Text.MaxLength) 180 | { 181 | Text.InsertCharacter(Cursor.TextCursor, c); 182 | Cursor.TextCursor++; 183 | } 184 | } 185 | } 186 | } 187 | break; 188 | } 189 | } 190 | } 191 | 192 | private void ShiftMod(int oldPos) 193 | { 194 | if (KeyboardInput.ShiftDown) 195 | { 196 | if (Cursor.SelectedChar == null) 197 | { 198 | Cursor.SelectedChar = oldPos; 199 | } 200 | } 201 | else 202 | { 203 | Cursor.SelectedChar = null; 204 | } 205 | } 206 | 207 | private void CharacterTyped(object sender, KeyboardInput.CharacterEventArgs e, KeyboardState ks) 208 | { 209 | if (Active && !KeyboardInput.CtrlDown) 210 | { 211 | if (IsLegalCharacter(Renderer.Font, e.Character) && !e.Character.Equals('\r') && 212 | !e.Character.Equals('\n')) 213 | { 214 | DelSelection(); 215 | if (Text.Length < Text.MaxLength) 216 | { 217 | Text.InsertCharacter(Cursor.TextCursor, e.Character); 218 | Cursor.TextCursor++; 219 | } 220 | } 221 | } 222 | } 223 | 224 | private string DelSelection(bool fakeForCopy = false) 225 | { 226 | if (!Cursor.SelectedChar.HasValue) 227 | { 228 | return null; 229 | } 230 | int tc = Cursor.TextCursor; 231 | int sc = Cursor.SelectedChar.Value; 232 | int min = Math.Min(sc, tc); 233 | int max = Math.Max(sc, tc); 234 | string result = Text.String.Substring(min, max - min); 235 | 236 | if (!fakeForCopy) 237 | { 238 | Text.Replace(Math.Min(sc, tc), Math.Max(sc, tc), string.Empty); 239 | if (Cursor.SelectedChar.Value < Cursor.TextCursor) 240 | { 241 | Cursor.TextCursor -= tc - sc; 242 | } 243 | Cursor.SelectedChar = null; 244 | } 245 | return result; 246 | } 247 | 248 | public static bool IsLegalCharacter(SpriteFont font, char c) 249 | { 250 | return font.Characters.Contains(c) || c == '\r' || c == '\n'; 251 | } 252 | 253 | public static int IndexOfNextCharAfterWhitespace(int pos, char[] characters) 254 | { 255 | char[] chars = characters; 256 | char c = chars[pos]; 257 | bool whiteSpaceFound = false; 258 | while (true) 259 | { 260 | if (c.Equals(' ')) 261 | { 262 | whiteSpaceFound = true; 263 | } 264 | else if (whiteSpaceFound) 265 | { 266 | return pos; 267 | } 268 | 269 | ++pos; 270 | if (pos >= chars.Length) 271 | { 272 | return chars.Length; 273 | } 274 | c = chars[pos]; 275 | } 276 | } 277 | 278 | public static int IndexOfLastCharBeforeWhitespace(int pos, char[] characters) 279 | { 280 | char[] chars = characters; 281 | 282 | bool charFound = false; 283 | while (true) 284 | { 285 | --pos; 286 | if (pos <= 0) 287 | { 288 | return 0; 289 | } 290 | var c = chars[pos]; 291 | 292 | if (c.Equals(' ')) 293 | { 294 | if (charFound) 295 | { 296 | return ++pos; 297 | } 298 | } 299 | else 300 | { 301 | charFound = true; 302 | } 303 | } 304 | } 305 | 306 | public void Update() 307 | { 308 | Renderer.Update(); 309 | Cursor.Update(); 310 | } 311 | 312 | public void Draw(SpriteBatch spriteBatch) 313 | { 314 | Renderer.Draw(spriteBatch); 315 | if (Active) 316 | { 317 | Cursor.Draw(spriteBatch); 318 | } 319 | } 320 | } 321 | } -------------------------------------------------------------------------------- /MonoGame-Textbox/TextRenderer.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using Microsoft.Xna.Framework; 29 | using Microsoft.Xna.Framework.Graphics; 30 | 31 | namespace MonoGame_Textbox 32 | { 33 | public class TextRenderer 34 | { 35 | public Rectangle Area { get; set; } 36 | public SpriteFont Font { get; set; } 37 | public Color Color { get; set; } 38 | 39 | private readonly TextBox box; 40 | private RenderTarget2D target; 41 | private SpriteBatch batch; 42 | 43 | // Cached texture that has all of the characters. 44 | private Texture2D text; 45 | 46 | // Location of the character. 47 | internal readonly short[] X; 48 | internal readonly short[] Y; 49 | 50 | // With of the character. 51 | internal readonly byte[] Width; 52 | 53 | // Row the character is on. 54 | private readonly byte[] row; 55 | 56 | public void Dispose() 57 | { 58 | text?.Dispose(); 59 | text = null; 60 | target?.Dispose(); 61 | target = null; 62 | Font = null; 63 | batch?.Dispose(); 64 | batch = null; 65 | } 66 | 67 | public TextRenderer(TextBox box) 68 | { 69 | this.box = box; 70 | 71 | X = new short[this.box.Text.MaxLength]; 72 | Y = new short[this.box.Text.MaxLength]; 73 | Width = new byte[this.box.Text.MaxLength]; 74 | 75 | row = new byte[this.box.Text.MaxLength]; 76 | } 77 | 78 | public void Update() 79 | { 80 | if (!box.Text.IsDirty) 81 | { 82 | return; 83 | } 84 | 85 | MeasureCharacterWidths(); 86 | text = RenderText(); 87 | } 88 | 89 | public void Draw(SpriteBatch spriteBatch) 90 | { 91 | if (text != null) 92 | { 93 | spriteBatch.Draw(text, Area, Color.White); 94 | } 95 | } 96 | 97 | public int CharAt(Point localLocation) 98 | { 99 | Rectangle charRectangle = new Rectangle(0, 0, 0, Font.LineSpacing); 100 | 101 | int r = localLocation.Y / (Font.LineSpacing); 102 | 103 | for (short i = 0; i < box.Text.Length; i++) 104 | { 105 | if (row[i] != r) 106 | { 107 | continue; 108 | } 109 | 110 | // Rectangle that encompasses the current character. 111 | charRectangle.X = X[i]; 112 | charRectangle.Y = Y[i]; 113 | charRectangle.Width = Width[i]; 114 | 115 | // Click on a character so put the cursor in front of it. 116 | if (charRectangle.Contains(localLocation)) 117 | { 118 | return i; 119 | } 120 | 121 | // Next character is not on the correct row so this is the last character for this row so select it. 122 | if (i < box.Text.Length - 1 && row[i + 1] != r) 123 | { 124 | return i; 125 | } 126 | } 127 | 128 | // Missed a character so return the end. 129 | return box.Text.Length; 130 | } 131 | 132 | private void MeasureCharacterWidths() 133 | { 134 | for (int i = 0; i < box.Text.Length; i++) 135 | { 136 | Width[i] = MeasureCharacter(i); 137 | } 138 | } 139 | 140 | private byte MeasureCharacter(int location) 141 | { 142 | string value = box.Text.String; 143 | float front = Font.MeasureString(value.Substring(0, location)).X; 144 | float end = Font.MeasureString(value.Substring(0, location + 1)).X; 145 | 146 | return (byte) (end - front); 147 | } 148 | 149 | private Texture2D RenderText() 150 | { 151 | if (batch == null) 152 | { 153 | batch = new SpriteBatch(box.GraphicsDevice); 154 | } 155 | if (target == null) 156 | { 157 | target = new RenderTarget2D(box.GraphicsDevice, Area.Width, Area.Height); 158 | } 159 | 160 | box.GraphicsDevice.SetRenderTarget(target); 161 | 162 | box.GraphicsDevice.Clear(Color.Transparent); 163 | 164 | int start = 0; 165 | float height = 0.0f; 166 | 167 | batch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend); 168 | 169 | while (true) 170 | { 171 | start = RenderLine(batch, start, height); 172 | 173 | if (start >= box.Text.Length) 174 | { 175 | batch.End(); 176 | box.GraphicsDevice.SetRenderTarget(null); 177 | 178 | return target; 179 | } 180 | 181 | height += Font.LineSpacing; 182 | } 183 | } 184 | 185 | private int RenderLine(SpriteBatch spriteBatch, int start, float height) 186 | { 187 | int breakLocation = start; 188 | float lineLength = 0.0f; 189 | byte r = (byte) (height / Font.LineSpacing); 190 | 191 | string t = box.Text.String; 192 | string tempText; 193 | 194 | // Starting from end of last line loop though the characters. 195 | for (int iCount = start; iCount < box.Text.Length; iCount++) 196 | { 197 | // Calculate screen location of current character. 198 | X[iCount] = (short) lineLength; 199 | Y[iCount] = (short) height; 200 | row[iCount] = r; 201 | 202 | // Calculate the width of the current line. 203 | lineLength += Width[iCount]; 204 | 205 | // Current line is too long need to split it. 206 | if (lineLength > Area.Width) 207 | { 208 | if (breakLocation == start) 209 | { 210 | // Have to split a word. 211 | // Render line and return start of new line. 212 | tempText = t.Substring(start, iCount - start); 213 | spriteBatch.DrawString(Font, tempText, new Vector2(0.0f, height), Color); 214 | return iCount + 1; 215 | } 216 | 217 | // Have a character we can split on. 218 | // Render line and return start of new line. 219 | tempText = t.Substring(start, breakLocation - start); 220 | spriteBatch.DrawString(Font, tempText, new Vector2(0.0f, height), Color); 221 | return breakLocation + 1; 222 | } 223 | 224 | // Handle characters that force/allow for breaks. 225 | switch (box.Text.Characters[iCount]) 226 | { 227 | // These characters force a line break. 228 | case '\r': 229 | case '\n': 230 | //Render line and return start of new line. 231 | tempText = t.Substring(start, iCount - start); 232 | spriteBatch.DrawString(Font, tempText, new Vector2(0.0f, height), Color); 233 | return iCount + 1; 234 | // These characters are good break locations. 235 | case '-': 236 | case ' ': 237 | breakLocation = iCount + 1; 238 | break; 239 | } 240 | } 241 | 242 | // We hit the end of the text box render line and return 243 | // _textData.Length so RenderText knows to return. 244 | tempText = t.Substring(start, box.Text.Length - start); 245 | spriteBatch.DrawString(Font, tempText, new Vector2(0.0f, height), Color); 246 | return box.Text.Length; 247 | } 248 | } 249 | } -------------------------------------------------------------------------------- /MonoGame-Textbox/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![NuGet](https://img.shields.io/nuget/v/MonoGame-Textbox.svg?maxAge=2592000)](https://www.nuget.org/packages/MonoGame-Textbox/) [![NuGet](https://img.shields.io/nuget/dt/MonoGame-Textbox.svg?maxAge=2592000)](https://www.nuget.org/packages/MonoGame-Textbox/) 2 | [![license](https://img.shields.io/github/license/unterrainerinformatik/MonoGame-Textbox.svg?maxAge=2592000)](http://unlicense.org) [![Twitter Follow](https://img.shields.io/twitter/follow/throbax.svg?style=social&label=Follow&maxAge=2592000)](https://twitter.com/throbax) 3 | 4 | # General 5 | 6 | This section contains various useful projects that should help your development-process. 7 | 8 | This section of our GIT repositories is free. You may copy, use or rewrite every single one of its contained projects to your hearts content. 9 | In order to get help with basic GIT commands you may try [the GIT cheat-sheet][coding] on our [homepage][homepage]. 10 | 11 | This repository located on our [homepage][homepage] is private since this is the master- and release-branch. You may clone it, but it will be read-only. 12 | If you want to contribute to our repository (push, open pull requests), please use the copy on github located here: [the public github repository][github] 13 | 14 | # ![Icon](https://github.com/UnterrainerInformatik/Monogame-Textbox/raw/master/icon.png)Monogame-Textbox 15 | 16 | This is a PCL that provides an editable, multi-language, event-driven textbox for MonoGame that supports selection, special characters, SHIFT-CTRL combinations with cursor-keys and cut/copy/paste. 17 | 18 | > **If you like this repo, please don't forget to star it.** 19 | > **Thank you.** 20 | 21 | 22 | 23 | ## Getting Started 24 | 25 | Clone the repository and run the project `Textbox-Test` as startup project. 26 | Nuget should take care about the rest... 27 | 28 | Write, select, delete, cut (ctrl-x), copy (ctrl-c), paste (ctrl-v), press **home**, press **pos1**, keep **shift** pressed while pressing a cursor key to select, try pressing **ctrl** while doing so in order to jump words ahead or back... 29 | 30 | ![Screenshot](https://github.com/UnterrainerInformatik/MonoGame-Textbox/blob/master/Textbox-Test.png) 31 | 32 | ### General Information 33 | 34 | I generate the SpriteFont used in the test-projects using a `.spritefont` description-file read by the content-pipeline tool because of the insane amount of supported glyphs. 35 | 36 | The reason why I don't use the automated build-pipeline for this is that it would require everyone to install the True-Type Font referenced further down this file. You are free to do so, in which case you may as well add the file `Arsenal.spritefont` to your automated build-pipeline of your project. 37 | 38 | #### Used Tools 39 | 40 | * [Resharper][https://www.jetbrains.com/resharper/] 41 | 42 | * [MonoGame](http://www.monogame.net/) 43 | 44 | * You can easily get rid of the `Primitives2D.cs` class when you incorporate it in your own project. It's only there to draw the outline of the textbox in the demo. 45 | 46 | * The font is called Arsenal and you can find it here [Fontsquirrel - Arsenal](https://www.fontsquirrel.com/fonts/arsenal?q%5Bterm%5D=arsenal&q%5Bsearch_check%5D=Y) 47 | 48 | It has an insane amount of supported languages (and therefore glyphs) and is under the SIL Open Font License v1.10. So it's safe to use in your games. 49 | 50 | 51 | 52 | [homepage]: http://www.unterrainer.info 53 | [coding]: http://www.unterrainer.info/Home/Coding 54 | [github]: https://github.com/UnterrainerInformatik/MonoGame-Textbox 55 | -------------------------------------------------------------------------------- /Textbox-Test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnterrainerInformatik/Monogame-Textbox/61ce94d05d877e30b7d78efcf475a9fcc73876d4/Textbox-Test.png -------------------------------------------------------------------------------- /Textbox-Test/Content/Arsenal.spritefont: -------------------------------------------------------------------------------- 1 |  2 | 8 | 9 | 10 | 11 | 14 | Arsenal 15 | 16 | 20 | 18 21 | 22 | 26 | 0 27 | 28 | 32 | true 33 | 34 | 38 | 39 | 40 | 44 | 45 | 46 | 53 | 54 | 55 | 56 | 57 |  58 | 59 | 60 | 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 | -------------------------------------------------------------------------------- /Textbox-Test/Content/Content.mgcb: -------------------------------------------------------------------------------- 1 | 2 | #----------------------------- Global Properties ----------------------------# 3 | 4 | /outputDir:bin/$(Platform) 5 | /intermediateDir:obj/$(Platform) 6 | /platform:Windows 7 | /config: 8 | /profile:Reach 9 | /compress:False 10 | 11 | #-------------------------------- References --------------------------------# 12 | 13 | 14 | #---------------------------------- Content ---------------------------------# 15 | 16 | #begin Arsenal.spritefont 17 | /importer:FontDescriptionImporter 18 | /processor:FontDescriptionProcessor 19 | /processorParam:PremultiplyAlpha=True 20 | /processorParam:TextureFormat=Compressed 21 | /build:Arsenal.spritefont 22 | 23 | -------------------------------------------------------------------------------- /Textbox-Test/Content/bin/Windows/Arsenal.xnb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnterrainerInformatik/Monogame-Textbox/61ce94d05d877e30b7d78efcf475a9fcc73876d4/Textbox-Test/Content/bin/Windows/Arsenal.xnb -------------------------------------------------------------------------------- /Textbox-Test/Game1.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using Microsoft.Xna.Framework; 29 | using Microsoft.Xna.Framework.Graphics; 30 | using Microsoft.Xna.Framework.Input; 31 | using MonoGame_Textbox; 32 | 33 | namespace Textbox_Test 34 | { 35 | /// 36 | /// This is the main type for your game. 37 | /// 38 | public class Game1 : Game 39 | { 40 | GraphicsDeviceManager graphics; 41 | SpriteBatch spriteBatch; 42 | 43 | private TextBox textBox; 44 | private SpriteFont font; 45 | private Rectangle viewport; 46 | 47 | public Game1() 48 | { 49 | GraphicsDeviceManager graphics = new GraphicsDeviceManager(this); 50 | Content.RootDirectory = "Content"; 51 | 52 | // Initialize the keyboard-event handler. 53 | KeyboardInput.Initialize(this, 500f, 20); 54 | } 55 | 56 | /// 57 | /// LoadContent will be called once per game and is the place to load 58 | /// all of your content. 59 | /// 60 | protected override void LoadContent() 61 | { 62 | // Create a new SpriteBatch, which can be used to draw textures. 63 | spriteBatch = new SpriteBatch(GraphicsDevice); 64 | 65 | font = Content.Load("Arsenal"); 66 | 67 | viewport = new Rectangle(50, 50, 400, 200); 68 | textBox = new TextBox(viewport, 200, "This is a test. Move the cursor, select, delete, write...", 69 | GraphicsDevice, font, Color.LightGray, Color.DarkGreen, 30); 70 | } 71 | 72 | /// 73 | /// Allows the game to run logic such as updating the world, 74 | /// checking for collisions, gathering input, and playing audio. 75 | /// 76 | /// Provides a snapshot of timing values. 77 | protected override void Update(GameTime gameTime) 78 | { 79 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || 80 | Keyboard.GetState().IsKeyDown(Keys.Escape)) 81 | Exit(); 82 | 83 | KeyboardInput.Update(); 84 | 85 | // This code is in here to play with in debug mode. 86 | // You may as well initialize it in LoadContent() or change it in here to achieve lerp effects and so on... 87 | float margin = 3; 88 | textBox.Area = new Rectangle((int) (viewport.X + margin), viewport.Y, (int) (viewport.Width - margin), 89 | viewport.Height); 90 | textBox.Renderer.Color = Color.White; 91 | textBox.Cursor.Selection = new Color(Color.Purple, .4f); 92 | 93 | float lerpAmount = (float) (gameTime.TotalGameTime.TotalMilliseconds % 500f / 500f); 94 | textBox.Cursor.Color = Color.Lerp(Color.DarkGray, Color.LightGray, lerpAmount); 95 | 96 | textBox.Active = true; 97 | textBox.Update(); 98 | 99 | base.Update(gameTime); 100 | } 101 | 102 | /// 103 | /// This is called when the game should draw itself. 104 | /// 105 | /// Provides a snapshot of timing values. 106 | protected override void Draw(GameTime gameTime) 107 | { 108 | GraphicsDevice.Clear(Color.DarkSlateGray); 109 | 110 | spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); 111 | textBox.Draw(spriteBatch); 112 | spriteBatch.DrawRectangle(viewport, Color.Red, 1f, 1f); 113 | spriteBatch.End(); 114 | 115 | base.Draw(gameTime); 116 | } 117 | } 118 | } -------------------------------------------------------------------------------- /Textbox-Test/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnterrainerInformatik/Monogame-Textbox/61ce94d05d877e30b7d78efcf475a9fcc73876d4/Textbox-Test/Icon.ico -------------------------------------------------------------------------------- /Textbox-Test/Primitives2D.cs: -------------------------------------------------------------------------------- 1 | // *************************************************************************** 2 | // This is free and unencumbered software released into the public domain. 3 | // 4 | // Anyone is free to copy, modify, publish, use, compile, sell, or 5 | // distribute this software, either in source code form or as a compiled 6 | // binary, for any purpose, commercial or non-commercial, and by any 7 | // means. 8 | // 9 | // In jurisdictions that recognize copyright laws, the author or authors 10 | // of this software dedicate any and all copyright interest in the 11 | // software to the public domain. We make this dedication for the benefit 12 | // of the public at large and to the detriment of our heirs and 13 | // successors. We intend this dedication to be an overt act of 14 | // relinquishment in perpetuity of all present and future rights to this 15 | // software under copyright law. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | // OTHER DEALINGS IN THE SOFTWARE. 24 | // 25 | // For more information, please refer to 26 | // *************************************************************************** 27 | 28 | using System; 29 | using System.Collections.Generic; 30 | using Microsoft.Xna.Framework; 31 | using Microsoft.Xna.Framework.Graphics; 32 | 33 | namespace Textbox_Test 34 | { 35 | public static class Primitives2D 36 | { 37 | private static readonly Dictionary> circleCache = new Dictionary>(); 38 | private static readonly Dictionary> arcCache = new Dictionary>(); 39 | 40 | private static Texture2D pixel; 41 | 42 | public static Texture2D GetWhitePixel(this SpriteBatch spriteBatch) 43 | { 44 | if (pixel == null) 45 | { 46 | pixel = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, 47 | SurfaceFormat.Color); 48 | pixel.SetData(new[] {Color.White}); 49 | } 50 | return pixel; 51 | } 52 | 53 | /// 54 | /// Draws a list of connecting points. 55 | /// 56 | /// The destination drawing surface 57 | /// Where to position the points 58 | /// The points to connect with lines 59 | /// The color to use 60 | /// The thickness of the lines 61 | /// The layer depth. 62 | private static void DrawPoints(SpriteBatch spriteBatch, Vector2 position, List points, Color color, 63 | float thickness, float layerDepth) 64 | { 65 | if (points.Count < 2) 66 | return; 67 | 68 | for (var i = 1; i < points.Count; i++) 69 | { 70 | DrawLine(spriteBatch, point1: points[i - 1] + position, point2: points[i] + position, 71 | color: color, thickness: thickness, layerDepth: 1f / layerDepth); 72 | } 73 | } 74 | 75 | /// 76 | /// Creates a list of vectors that represents a circle. 77 | /// 78 | /// The radius of the circle 79 | /// The number of sides to generate 80 | /// A list of vectors that, if connected, will create a circle 81 | private static List CreateCircle(double radius, int sides) 82 | { 83 | // Look for a cached version of this circle. 84 | var circleKey = radius + "x" + sides; 85 | List result; 86 | if (circleCache.TryGetValue(circleKey, out result)) 87 | { 88 | return result; 89 | } 90 | 91 | result = new List(); 92 | const double max = 2.0 * Math.PI; 93 | var step = max / sides; 94 | 95 | for (var theta = 0.0; theta < max; theta += step) 96 | { 97 | result.Add(new Vector2((float) (radius * Math.Cos(theta)), (float) (radius * Math.Sin(theta)))); 98 | } 99 | 100 | // Then add the first vector again so it's a complete loop. 101 | result.Add(new Vector2((float) (radius * Math.Cos(0)), (float) (radius * Math.Sin(0)))); 102 | 103 | // Cache this circle so that it can be quickly drawn next time. 104 | circleCache.Add(circleKey, result); 105 | 106 | return result; 107 | } 108 | 109 | /// 110 | /// Creates a list of vectors that represents an arc. 111 | /// 112 | /// The radius of the arc 113 | /// The number of sides to generate in the circle that this will cut out from 114 | /// The starting angle of arc, 0 being to the east, increasing as you go clockwise 115 | /// The radians to draw, clockwise from the starting angle 116 | /// A list of vectors that, if connected, will create an arc 117 | private static List CreateArc(float radius, int sides, float startingAngle, float radians) 118 | { 119 | // Look for a cached version of this arc. 120 | var arcKey = radius + "x" + sides + "," + startingAngle + "," + radians; 121 | List result; 122 | if (arcCache.TryGetValue(arcKey, out result)) 123 | { 124 | return result; 125 | } 126 | 127 | result = new List(); 128 | result.AddRange(CreateCircle(radius, sides)); 129 | result.RemoveAt(result.Count - 1); // remove the last point because it's a duplicate of the first 130 | 131 | // The circle starts at (radius, 0) 132 | var curAngle = 0.0; 133 | double anglePerSide = MathHelper.TwoPi / sides; 134 | 135 | // "Rotate" to the starting point 136 | while (curAngle + anglePerSide / 2.0 < startingAngle) 137 | { 138 | curAngle += anglePerSide; 139 | 140 | // move the first point to the end 141 | result.Add(result[0]); 142 | result.RemoveAt(0); 143 | } 144 | 145 | // Add the first point, just in case we make a full circle 146 | result.Add(result[0]); 147 | 148 | // Now remove the points at the end of the circle to create the arc 149 | var sidesInArc = (int) (radians / anglePerSide + 0.5); 150 | result.RemoveRange(sidesInArc + 1, result.Count - sidesInArc - 1); 151 | 152 | // Cache this arc so that it can be quickly drawn next time. 153 | arcCache.Add(arcKey, result); 154 | 155 | return result; 156 | } 157 | 158 | /// 159 | /// Draws a filled rectangle. 160 | /// 161 | /// The destination drawing surface 162 | /// The rectangle to draw 163 | /// The color to draw the rectangle in 164 | /// The layer depth. 165 | public static void FillRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color, float layerDepth) 166 | { 167 | // Simply use the function already there 168 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), rect, null, 169 | color, 0f, Vector2.Zero, SpriteEffects.None, 170 | layerDepth: layerDepth == 0f ? 0f : 1f / layerDepth); 171 | } 172 | 173 | /// 174 | /// Draws a filled rectangle. 175 | /// 176 | /// The destination drawing surface 177 | /// The rectangle to draw 178 | /// The color to draw the rectangle in 179 | /// The angle in radians to draw the rectangle at 180 | /// The layer depth. 181 | public static void FillRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color, float angle, 182 | float layerDepth) 183 | { 184 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), rect, null, 185 | color, angle, Vector2.Zero, SpriteEffects.None, 186 | layerDepth: layerDepth == 0f ? 0f : 1f / layerDepth); 187 | } 188 | 189 | /// 190 | /// Draws a filled rectangle. 191 | /// 192 | /// The destination drawing surface 193 | /// Where to draw 194 | /// The size of the rectangle 195 | /// The color to draw the rectangle in 196 | /// The layer depth. 197 | public static void FillRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color, 198 | float layerDepth) 199 | { 200 | // stretch the pixel between the two vectors 201 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), location, null, color, 202 | 0f, Vector2.Zero, size, SpriteEffects.None, 203 | layerDepth: layerDepth == 0f ? 0f : 1f / layerDepth); 204 | } 205 | 206 | /// 207 | /// Draws a filled rectangle. 208 | /// 209 | /// The destination drawing surface 210 | /// Where to draw 211 | /// The size of the rectangle 212 | /// The color to draw the rectangle in 213 | /// The angle in radians to draw the rectangle at 214 | /// The layer depth. 215 | public static void FillRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color, 216 | float angle, float layerDepth) 217 | { 218 | // stretch the pixel between the two vectors 219 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), location, null, color, 220 | angle, Vector2.Zero, size, SpriteEffects.None, 221 | layerDepth: layerDepth == 0f ? 0f : 1f / layerDepth); 222 | } 223 | 224 | /// 225 | /// Draws a filled rectangle. 226 | /// 227 | /// The destination drawing surface 228 | /// The X coordinate of the left side 229 | /// The Y coordinate of the upper side 230 | /// Width 231 | /// Height 232 | /// The color to draw the rectangle in 233 | /// The layer depth. 234 | public static void FillRectangle(this SpriteBatch spriteBatch, float x, float y, float w, float h, Color color, 235 | float layerDepth) 236 | { 237 | FillRectangle(spriteBatch, location: new Vector2(x, y), size: new Vector2(w, h), 238 | color: color, angle: 0.0f, layerDepth: layerDepth); 239 | } 240 | 241 | /// 242 | /// Draws a filled rectangle. 243 | /// 244 | /// The destination drawing surface 245 | /// The X coordinate of the left side 246 | /// The Y coordinate of the upper side 247 | /// Width 248 | /// Height 249 | /// The color to draw the rectangle in 250 | /// The angle of the rectangle in radians 251 | /// The layer depth. 252 | public static void FillRectangle(this SpriteBatch spriteBatch, float x, float y, float w, float h, Color color, 253 | float angle, float layerDepth) 254 | { 255 | FillRectangle(spriteBatch, location: new Vector2(x, y), size: new Vector2(w, h), 256 | color: color, angle: angle, layerDepth: layerDepth); 257 | } 258 | 259 | /// 260 | /// Draws a rectangle with the thickness provided. 261 | /// 262 | /// The destination drawing surface 263 | /// The rectangle to draw 264 | /// The color to draw the rectangle in 265 | /// The layer depth. 266 | public static void DrawRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color, float layerDepth) 267 | { 268 | DrawRectangle(spriteBatch, rect, color, 1.0f, layerDepth); 269 | } 270 | 271 | /// 272 | /// Draws a rectangle with the thickness provided. 273 | /// 274 | /// The destination drawing surface 275 | /// The rectangle to draw 276 | /// The color to draw the rectangle in 277 | /// The thickness of the lines 278 | /// The layer depth. 279 | public static void DrawRectangle(this SpriteBatch spriteBatch, Rectangle rect, Color color, float thickness, 280 | float layerDepth) 281 | { 282 | // TODO: Handle rotations 283 | // TODO: Figure out the pattern for the offsets required and then handle it in the line instead of here 284 | 285 | DrawLine(spriteBatch, point1: new Vector2(rect.X, rect.Y), 286 | point2: new Vector2(rect.Right, rect.Y), color: color, thickness: thickness, 287 | layerDepth: layerDepth); // top 288 | DrawLine(spriteBatch, point1: new Vector2(rect.X + 1f, rect.Y), 289 | point2: new Vector2(rect.X + 1f, rect.Bottom + thickness), color: color, 290 | thickness: thickness, layerDepth: layerDepth); // left 291 | DrawLine(spriteBatch, point1: new Vector2(rect.X, rect.Bottom), 292 | point2: new Vector2(rect.Right, rect.Bottom), color: color, thickness: thickness, 293 | layerDepth: layerDepth); 294 | // bottom 295 | DrawLine(spriteBatch, point1: new Vector2(rect.Right + 1f, rect.Y), 296 | point2: new Vector2(rect.Right + 1f, rect.Bottom + thickness), 297 | color: color, thickness: thickness, layerDepth: layerDepth); // right 298 | } 299 | 300 | /// 301 | /// Draws a rectangle with the thickness provided 302 | /// 303 | /// The destination drawing surface 304 | /// Where to draw 305 | /// The size of the rectangle 306 | /// The color to draw the rectangle in 307 | /// The layer depth. 308 | public static void DrawRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color, 309 | float layerDepth) 310 | { 311 | DrawRectangle(spriteBatch, 312 | rect: new Rectangle((int) location.X, (int) location.Y, (int) size.X, (int) size.Y), color: color, 313 | thickness: 1.0f, layerDepth: layerDepth); 314 | } 315 | 316 | /// 317 | /// Draws a rectangle with the thickness provided. 318 | /// 319 | /// The destination drawing surface 320 | /// Where to draw 321 | /// The size of the rectangle 322 | /// The color to draw the rectangle in 323 | /// The thickness of the line 324 | /// The layer depth. 325 | public static void DrawRectangle(this SpriteBatch spriteBatch, Vector2 location, Vector2 size, Color color, 326 | float thickness, float layerDepth) 327 | { 328 | DrawRectangle(spriteBatch, 329 | rect: new Rectangle((int) location.X, (int) location.Y, (int) size.X, (int) size.Y), color: color, 330 | thickness: thickness, layerDepth: layerDepth); 331 | } 332 | 333 | /// 334 | /// Draws a line from point1 to point2. 335 | /// 336 | /// The destination drawing surface 337 | /// The X coordinate of the first point 338 | /// The Y coordinate of the first point 339 | /// The X coordinate of the second point 340 | /// The Y coordinate of the second point 341 | /// The color to use 342 | /// The layer depth. 343 | public static void DrawLine(this SpriteBatch spriteBatch, float x1, float y1, float x2, float y2, Color color, 344 | float layerDepth) 345 | { 346 | DrawLine(spriteBatch, point1: new Vector2(x1, y1), point2: new Vector2(x2, y2), 347 | color: color, thickness: 1f, layerDepth: layerDepth); 348 | } 349 | 350 | /// 351 | /// Draws a line from point1 to point2. 352 | /// 353 | /// The destination drawing surface 354 | /// The X coordinate of the first point 355 | /// The Y coordinate of the first point 356 | /// The X coordinate of the second point 357 | /// The Y coordinate of the second point 358 | /// The color to use 359 | /// The thickness of the line 360 | /// The layer depth. 361 | public static void DrawLine(this SpriteBatch spriteBatch, float x1, float y1, float x2, float y2, Color color, 362 | float thickness, float layerDepth) 363 | { 364 | DrawLine(spriteBatch, point1: new Vector2(x1, y1), point2: new Vector2(x2, y2), 365 | color: color, thickness: thickness, layerDepth: layerDepth); 366 | } 367 | 368 | /// 369 | /// Draws a line from point1 to point2. 370 | /// 371 | /// The destination drawing surface 372 | /// The first point 373 | /// The second point 374 | /// The color to use 375 | /// The layer depth. 376 | public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, 377 | float layerDepth) 378 | { 379 | DrawLine(spriteBatch, point1, point2, color, 1f, 380 | layerDepth); 381 | } 382 | 383 | /// 384 | /// Draws a line from point1 to point2 with a thickness. 385 | /// 386 | /// The destination drawing surface 387 | /// The first point 388 | /// The second point 389 | /// The color to use 390 | /// The thickness of the line 391 | /// The layer depth. 392 | public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point1, Vector2 point2, Color color, 393 | float thickness, float layerDepth) 394 | { 395 | // calculate the distance between the two vectors 396 | var distance = Vector2.Distance(point1, point2); 397 | 398 | // calculate the angle between the two vectors 399 | var angle = (float) Math.Atan2(point2.Y - point1.Y, point2.X - point1.X); 400 | 401 | DrawLine(spriteBatch, point1, distance, angle, color, 402 | thickness, layerDepth); 403 | } 404 | 405 | /// 406 | /// Draws a line from point1 to point2. 407 | /// 408 | /// The destination drawing surface 409 | /// The starting point 410 | /// The length of the line 411 | /// The angle of this line from the starting point in radians 412 | /// The color to use 413 | /// The layer depth. 414 | public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point, float length, float angle, Color color, 415 | float layerDepth) 416 | { 417 | DrawLine(spriteBatch, point, length, angle, color, 1.0f, 418 | layerDepth); 419 | } 420 | 421 | /// 422 | /// Draws a line from point1 to point2. 423 | /// 424 | /// The destination drawing surface 425 | /// The starting point 426 | /// The length of the line 427 | /// The angle of this line from the starting point 428 | /// The color to use 429 | /// The thickness of the line 430 | /// The layer depth. 431 | public static void DrawLine(this SpriteBatch spriteBatch, Vector2 point, float length, float angle, Color color, 432 | float thickness, float layerDepth) 433 | { 434 | // stretch the pixel between the two vectors 435 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), 436 | point, 437 | null, 438 | color, 439 | angle, 440 | Vector2.Zero, 441 | scale: new Vector2(length, thickness), 442 | effects: SpriteEffects.None, layerDepth: layerDepth == 0f ? 0f : 1f / layerDepth); 443 | } 444 | 445 | public static void PutPixel(this SpriteBatch spriteBatch, float x, float y, Color color) 446 | { 447 | PutPixel(spriteBatch, position: new Vector2(x, y), color: color); 448 | } 449 | 450 | public static void PutPixel(this SpriteBatch spriteBatch, Vector2 position, Color color) 451 | { 452 | spriteBatch.Draw(spriteBatch.GetWhitePixel(), position, color); 453 | } 454 | 455 | /// 456 | /// Draw a circle. 457 | /// 458 | /// The destination drawing surface 459 | /// The center of the circle 460 | /// The radius of the circle 461 | /// The number of sides to generate 462 | /// The color of the circle 463 | /// The layer depth. 464 | public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, 465 | float layerDepth) 466 | { 467 | DrawPoints(spriteBatch, center, points: CreateCircle(radius, sides), 468 | color: color, thickness: 1.0f, layerDepth: layerDepth); 469 | } 470 | 471 | /// 472 | /// Draw a circle. 473 | /// 474 | /// The destination drawing surface 475 | /// The center of the circle 476 | /// The radius of the circle 477 | /// The number of sides to generate 478 | /// The color of the circle 479 | /// The thickness of the lines used 480 | /// The layer depth. 481 | public static void DrawCircle(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, Color color, 482 | float thickness, float layerDepth) 483 | { 484 | DrawPoints(spriteBatch, center, points: CreateCircle(radius, sides), 485 | color: color, thickness: thickness, layerDepth: layerDepth); 486 | } 487 | 488 | /// 489 | /// Draw a circle. 490 | /// 491 | /// The destination drawing surface 492 | /// The center X of the circle 493 | /// The center Y of the circle 494 | /// The radius of the circle 495 | /// The number of sides to generate 496 | /// The color of the circle 497 | /// The layer depth. 498 | public static void DrawCircle(this SpriteBatch spriteBatch, float x, float y, float radius, int sides, 499 | Color color, float layerDepth) 500 | { 501 | DrawPoints(spriteBatch, position: new Vector2(x, y), 502 | points: CreateCircle(radius, sides), color: color, thickness: 1.0f, 503 | layerDepth: layerDepth); 504 | } 505 | 506 | /// 507 | /// Draw a circle. 508 | /// 509 | /// The destination drawing surface 510 | /// The center X of the circle 511 | /// The center Y of the circle 512 | /// The radius of the circle 513 | /// The number of sides to generate 514 | /// The color of the circle 515 | /// The thickness of the lines used 516 | /// The layer depth. 517 | public static void DrawCircle(this SpriteBatch spriteBatch, float x, float y, float radius, int sides, 518 | Color color, 519 | float thickness, float layerDepth) 520 | { 521 | DrawPoints(spriteBatch, position: new Vector2(x, y), 522 | points: CreateCircle(radius, sides), color: color, thickness: thickness, 523 | layerDepth: layerDepth); 524 | } 525 | 526 | /// 527 | /// Draw a arc. 528 | /// 529 | /// The destination drawing surface 530 | /// The center of the arc 531 | /// The radius of the arc 532 | /// The number of sides to generate 533 | /// The starting angle of arc, 0 being to the east, increasing as you go clockwise 534 | /// The number of radians to draw, clockwise from the starting angle 535 | /// The color of the arc 536 | /// The layer depth. 537 | public static void DrawArc(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, 538 | float startingAngle, 539 | float radians, Color color, float layerDepth) 540 | { 541 | DrawArc(spriteBatch, center, radius, sides, startingAngle, 542 | radians, color, 1.0f, layerDepth); 543 | } 544 | 545 | /// 546 | /// Draw a arc. 547 | /// 548 | /// The destination drawing surface 549 | /// The center of the arc 550 | /// The radius of the arc 551 | /// The number of sides to generate 552 | /// The starting angle of arc, 0 being to the east, increasing as you go clockwise 553 | /// The number of radians to draw, clockwise from the starting angle 554 | /// The color of the arc 555 | /// The thickness of the arc 556 | /// The layer depth. 557 | public static void DrawArc(this SpriteBatch spriteBatch, Vector2 center, float radius, int sides, 558 | float startingAngle, 559 | float radians, Color color, float thickness, float layerDepth) 560 | { 561 | var arc = CreateArc(radius, sides, startingAngle, radians); 562 | //List arc = CreateArc2(radius, sides, startingAngle, degrees); 563 | DrawPoints(spriteBatch, center, arc, color, thickness, 564 | layerDepth); 565 | } 566 | } 567 | } -------------------------------------------------------------------------------- /Textbox-Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Textbox_Test 4 | { 5 | #if WINDOWS || LINUX 6 | /// 7 | /// The main class. 8 | /// 9 | public static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | using (var game = new Game1()) 18 | game.Run(); 19 | } 20 | } 21 | #endif 22 | } 23 | -------------------------------------------------------------------------------- /Textbox-Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Textbox-Test")] 9 | [assembly: AssemblyProduct("Textbox-Test")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyDescription("Testproject")] 12 | [assembly: AssemblyCompany("Unterrainer Informatik OG")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f13fc452-1c64-48e0-b40c-24c7302f98b6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.1.0")] 36 | [assembly: AssemblyFileVersion("1.0.1.0")] 37 | -------------------------------------------------------------------------------- /Textbox-Test/Textbox-Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | 8.0.30703 8 | 2.0 9 | {D42297D9-5678-4449-BBFF-0424B35F6762} 10 | WinExe 11 | Properties 12 | Textbox_Test 13 | Textbox-Test 14 | 512 15 | Windows 16 | v4.6.1 17 | 18 | 19 | 20 | x86 21 | true 22 | full 23 | false 24 | bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ 25 | DEBUG;TRACE;WINDOWS 26 | prompt 27 | 4 28 | 29 | 30 | x86 31 | pdbonly 32 | true 33 | bin\$(MonoGamePlatform)\$(Platform)\$(Configuration)\ 34 | TRACE;WINDOWS 35 | prompt 36 | 4 37 | 38 | 39 | Icon.ico 40 | 41 | 42 | app.manifest 43 | 44 | 45 | Textbox_Test.Program 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | ..\MonoGame-Textbox\packages\JetBrains.Annotations.11.1.0\lib\net20\JetBrains.Annotations.dll 56 | True 57 | 58 | 59 | ..\MonoGame-Textbox\packages\MonoGame.Framework.DesktopGL.3.6.0.1625\lib\net40\MonoGame.Framework.dll 60 | True 61 | 62 | 63 | 64 | 65 | 66 | 67 | x64\SDL2.dll 68 | PreserveNewest 69 | 70 | 71 | x64\soft_oal.dll 72 | PreserveNewest 73 | 74 | 75 | x86\SDL2.dll 76 | PreserveNewest 77 | 78 | 79 | x86\soft_oal.dll 80 | PreserveNewest 81 | 82 | 83 | 84 | 85 | 86 | 87 | libopenal.1.dylib 88 | 89 | 90 | libSDL2-2.0.0.dylib 91 | 92 | 93 | MonoGame.Framework.dll.config 94 | PreserveNewest 95 | 96 | 97 | x64\libopenal.so.1 98 | PreserveNewest 99 | 100 | 101 | x64\libSDL2-2.0.so.0 102 | PreserveNewest 103 | 104 | 105 | x86\libopenal.so.1 106 | PreserveNewest 107 | 108 | 109 | x86\libSDL2-2.0.so.0 110 | PreserveNewest 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | {60845196-31b8-487a-8f25-83eac47da83d} 120 | MonoGame-Textbox 121 | 122 | 123 | 124 | 125 | 132 | -------------------------------------------------------------------------------- /Textbox-Test/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Textbox-Test/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | true/pm 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Textbox-Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnterrainerInformatik/Monogame-Textbox/61ce94d05d877e30b7d78efcf475a9fcc73876d4/icon.png -------------------------------------------------------------------------------- /nuget_pack.bat: -------------------------------------------------------------------------------- 1 | nuget pack -sym MonoGame-Textbox\MonoGame-Textbox.csproj 2 | rem When targeting release-builds, add: -Prop Configuration=Release -------------------------------------------------------------------------------- /nuget_push.bat: -------------------------------------------------------------------------------- 1 | nuget push %1 -source https://www.nuget.org 2 | rem https://nuget.smbsrc.net/ --------------------------------------------------------------------------------