├── .gitignore
├── Iguina.Demo.MonoGame
├── .config
│ └── dotnet-tools.json
├── Content
│ ├── Content.mgcb
│ ├── default_font.spritefont
│ └── disabled_effect.fx
├── Game1.cs
├── Icon.bmp
├── Icon.ico
├── Iguina.Demo.MonoGame.csproj
├── MonoGameInput.cs
├── MonoGameRenderer.cs
├── Program.cs
├── ReadmeExample.cs
└── app.manifest
├── Iguina.Demo.RayLib
├── Iguina.Demo.RayLib.csproj
├── Program.cs
├── RayLibInput.cs
├── RayLibRenderer.cs
├── ReadmeExample.cs
└── readme.md
├── Iguina.Demo
├── Assets
│ └── DefaultTheme
│ │ ├── Styles
│ │ ├── button.json
│ │ ├── checkbox.json
│ │ ├── color_picker.json
│ │ ├── color_picker_handle.json
│ │ ├── color_slider_horizontal.json
│ │ ├── color_slider_horizontal_handle.json
│ │ ├── dropdown_icon.json
│ │ ├── horizontal_line.json
│ │ ├── label.json
│ │ ├── list_item.json
│ │ ├── list_panel.json
│ │ ├── message_box_backdrop.json
│ │ ├── numeric_input.json
│ │ ├── numeric_input_button.json
│ │ ├── panel.json
│ │ ├── panel_title.json
│ │ ├── paragraph.json
│ │ ├── paragraph_base_style.json
│ │ ├── progress_bar_horizontal.json
│ │ ├── progress_bar_horizontal_alt.json
│ │ ├── progress_bar_horizontal_alt_fill.json
│ │ ├── progress_bar_horizontal_fill.json
│ │ ├── radio_button.json
│ │ ├── scrollbar_vertical.json
│ │ ├── scrollbar_vertical_handle.json
│ │ ├── slider_handle.json
│ │ ├── slider_horizontal.json
│ │ ├── slider_vertical.json
│ │ ├── text_input.json
│ │ ├── title.json
│ │ └── vertical_line.json
│ │ ├── Textures
│ │ ├── Icons.png
│ │ └── UI.png
│ │ └── system_style.json
├── Iguina.Demo.csproj
├── IguinaDemoStarter.cs
└── readme.md
├── Iguina.Tests
├── Iguina.Tests.csproj
├── NumericInputTests.cs
├── TestInputProvider.cs
└── TestRenderer.cs
├── Iguina.sln
├── Iguina
├── Defs
│ ├── Anchor.cs
│ ├── Color.cs
│ ├── CursorProperties.cs
│ ├── EntityState.cs
│ ├── FramedTexture.cs
│ ├── IconTexture.cs
│ ├── InputState.cs
│ ├── MeasureUnits.cs
│ ├── Orientation.cs
│ ├── OverflowMode.cs
│ ├── Point.cs
│ ├── Rectangle.cs
│ ├── Sides.cs
│ ├── StretchedTexture.cs
│ ├── StyleSheet.cs
│ ├── SystemStyleSheet.cs
│ └── TextAlignment.cs
├── Drivers
│ ├── IFilesProvider.cs
│ ├── IInputProvider.cs
│ └── IRenderer.cs
├── Entities
│ ├── Button.cs
│ ├── Checkbox.cs
│ ├── CheckedEntity.cs
│ ├── ColorPicker.cs
│ ├── ColorSlider.cs
│ ├── DropDown.cs
│ ├── Entity.cs
│ ├── HorizontalLine.cs
│ ├── IColorPicker.cs
│ ├── Label.cs
│ ├── ListBox.cs
│ ├── NumericInput.cs
│ ├── Panel.cs
│ ├── Paragraph.cs
│ ├── ProgressBar.cs
│ ├── RadioButton.cs
│ ├── RowsSpacer.cs
│ ├── Slider.cs
│ ├── TextInput.cs
│ ├── Title.cs
│ └── VerticalLine.cs
├── Iguina.csproj
├── Properties
│ └── PublishProfiles
│ │ └── FolderProfile.pubxml
├── UISystem.cs
├── Utils
│ ├── DrawUtils.cs
│ ├── MathUtils.cs
│ └── MessageBoxUtils.cs
└── readme.md
├── LICENSE
├── ReadmeAssets
├── anchors.png
├── demo.gif
├── entity-button.png
├── entity-checkbox.png
├── entity-colorpicker.png
├── entity-colorslider.png
├── entity-dropdown.png
├── entity-hl.png
├── entity-list.png
├── entity-numericinput.png
├── entity-panel.png
├── entity-paragraph.png
├── entity-progressbars.png
├── entity-sliders.png
├── entity-textinput.png
├── entity-vl.png
├── logo.png
├── logo.svg
├── logo_bg.png
├── logo_no_text.png
├── mg_basic_example.png
├── rl_basic_example.png
└── save-file-dialog.png
├── TODO.txt
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Download this file using PowerShell v3 under Windows with the following comand:
2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore
3 | # or wget:
4 | # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.sln.docstates
10 |
11 | # blender temp files
12 | *.blend1
13 |
14 | # Build results
15 |
16 | [Dd]ebug/
17 | [Rr]elease/
18 | x64/
19 | build/
20 | [Bb]in/
21 | [Oo]bj/
22 |
23 | # NuGet Packages
24 | *.nupkg
25 | # The packages folder can be ignored because of Package Restore
26 | **/packages/*
27 | # except build/, which is used as an MSBuild target.
28 | !**/packages/build/
29 | # Uncomment if necessary however generally it will be regenerated when needed
30 | #!**/packages/repositories.config
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | *_i.c
37 | *_p.c
38 | *.ilk
39 | *.meta
40 | # *.obj
41 | *.pch
42 | *.pdb
43 | *.pgc
44 | *.pgd
45 | *.rsp
46 | *.sbr
47 | *.tlb
48 | *.tli
49 | *.tlh
50 | *.tmp
51 | *.tmp_proj
52 | *.log
53 | *.vspscc
54 | *.vssscc
55 | .builds
56 | *.pidb
57 | *.log
58 | *.scc
59 |
60 | # OS generated files #
61 | .DS_Store*
62 | Icon?
63 |
64 | # Visual C++ cache files
65 | ipch/
66 | *.aps
67 | *.ncb
68 | *.opensdf
69 | *.sdf
70 | *.cachefile
71 |
72 | # Visual Studio profiler
73 | *.psess
74 | *.vsp
75 | *.vspx
76 |
77 | # Guidance Automation Toolkit
78 | *.gpState
79 |
80 | # ReSharper is a .NET coding add-in
81 | _ReSharper*/
82 | *.[Rr]e[Ss]harper
83 |
84 | # TeamCity is a build add-in
85 | _TeamCity*
86 |
87 | # DotCover is a Code Coverage Tool
88 | *.dotCover
89 |
90 | # NCrunch
91 | *.ncrunch*
92 | .*crunch*.local.xml
93 |
94 | # Installshield output folder
95 | [Ee]xpress/
96 |
97 | # DocProject is a documentation generator add-in
98 | DocProject/buildhelp/
99 | DocProject/Help/*.HxT
100 | DocProject/Help/*.HxC
101 | DocProject/Help/*.hhc
102 | DocProject/Help/*.hhk
103 | DocProject/Help/*.hhp
104 | DocProject/Help/Html2
105 | DocProject/Help/html
106 |
107 | # Click-Once directory
108 | publish/
109 |
110 | # Publish Web Output
111 | *.Publish.xml
112 |
113 | # Windows Azure Build Output
114 | csx
115 | *.build.csdef
116 |
117 | # Windows Store app package directory
118 | AppPackages/
119 |
120 | # Others
121 | *.Cache
122 | ClientBin/
123 | [Ss]tyle[Cc]op.*
124 | ~$*
125 | *~
126 | *.dbmdl
127 | *.[Pp]ublish.xml
128 | *.pfx
129 | *.publishsettings
130 | modulesbin/
131 | tempbin/
132 |
133 | # EPiServer Site file (VPP)
134 | AppData/
135 |
136 | # RIA/Silverlight projects
137 | Generated_Code/
138 |
139 | # Backup & report files from converting an old project file to a newer
140 | # Visual Studio version. Backup files are not needed, because we have git ;-)
141 | _UpgradeReport_Files/
142 | Backup*/
143 | UpgradeLog*.XML
144 | UpgradeLog*.htm
145 |
146 | # vim
147 | *.txt~
148 | *.swp
149 | *.swo
150 |
151 | # svn
152 | .svn
153 |
154 | # Remainings from resolvings conflicts in Source Control
155 | *.orig
156 |
157 | # SQL Server files
158 | **/App_Data/*.mdf
159 | **/App_Data/*.ldf
160 | **/App_Data/*.sdf
161 |
162 |
163 | #LightSwitch generated files
164 | GeneratedArtifacts/
165 | _Pvt_Extensions/
166 | ModelManifest.xml
167 |
168 | # =========================
169 | # Windows detritus
170 | # =========================
171 |
172 | # Windows image file caches
173 | Thumbs.db
174 | ehthumbs.db
175 |
176 | # Folder config file
177 | Desktop.ini
178 |
179 | # Recycle Bin used on file shares
180 | $RECYCLE.BIN/
181 |
182 | # Mac desktop service store files
183 | .DS_Store
184 |
185 | # SASS Compiler cache
186 | .sass-cache
187 |
188 | # Visual Studio 2014 CTP
189 | **/*.sln.ide
190 |
191 | # Visual Studio temp something
192 | .vs/
193 |
194 | # VS 2015+
195 | *.vc.vc.opendb
196 | *.vc.db
197 |
198 | # Rider
199 | .idea/
200 |
201 | # Output folder used by Webpack or other FE stuff
202 | **/node_modules/*
203 | #**/wwwroot/*
204 |
205 | # SpecFlow specific
206 | *.feature.cs
207 | *.feature.xlsx.*
208 | *.Specs_*.html
209 |
210 | #####
211 | # End of core ignore list, below put you custom 'per project' settings (patterns or path)
212 | #####
213 | nuget.exe
214 |
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-mgcb": {
6 | "version": "3.8.1.303",
7 | "commands": [
8 | "mgcb"
9 | ]
10 | },
11 | "dotnet-mgcb-editor": {
12 | "version": "3.8.1.303",
13 | "commands": [
14 | "mgcb-editor"
15 | ]
16 | },
17 | "dotnet-mgcb-editor-linux": {
18 | "version": "3.8.1.303",
19 | "commands": [
20 | "mgcb-editor-linux"
21 | ]
22 | },
23 | "dotnet-mgcb-editor-windows": {
24 | "version": "3.8.1.303",
25 | "commands": [
26 | "mgcb-editor-windows"
27 | ]
28 | },
29 | "dotnet-mgcb-editor-mac": {
30 | "version": "3.8.1.303",
31 | "commands": [
32 | "mgcb-editor-mac"
33 | ]
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Content/Content.mgcb:
--------------------------------------------------------------------------------
1 |
2 | #----------------------------- Global Properties ----------------------------#
3 |
4 | /outputDir:bin/$(Platform)
5 | /intermediateDir:obj/$(Platform)
6 | /platform:DesktopGL
7 | /config:
8 | /profile:Reach
9 | /compress:False
10 |
11 | #-------------------------------- References --------------------------------#
12 |
13 |
14 | #---------------------------------- Content ---------------------------------#
15 |
16 | #begin default_font.spritefont
17 | /importer:FontDescriptionImporter
18 | /processor:FontDescriptionProcessor
19 | /processorParam:PremultiplyAlpha=True
20 | /processorParam:TextureFormat=Compressed
21 | /build:default_font.spritefont
22 |
23 | #begin disabled_effect.fx
24 | /importer:EffectImporter
25 | /processor:EffectProcessor
26 | /processorParam:DebugMode=Auto
27 | /build:disabled_effect.fx;disabled.fx
28 |
29 |
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Content/default_font.spritefont:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
14 | Arial
15 |
16 |
20 | 24
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 |
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Content/disabled_effect.fx:
--------------------------------------------------------------------------------
1 | #if OPENGL
2 | #define SV_POSITION POSITION
3 | #define VS_SHADERMODEL vs_3_0
4 | #define PS_SHADERMODEL ps_3_0
5 | #else
6 | #define VS_SHADERMODEL vs_4_0_level_9_1
7 | #define PS_SHADERMODEL ps_4_0_level_9_1
8 | #endif
9 |
10 | Texture2D SpriteTexture;
11 |
12 | sampler2D SpriteTextureSampler = sampler_state
13 | {
14 | Texture = ;
15 | };
16 |
17 | struct VertexShaderOutput
18 | {
19 | float4 Position : SV_POSITION;
20 | float4 Color : COLOR0;
21 | float2 TextureCoordinates : TEXCOORD0;
22 | };
23 |
24 | float4 MainPS(VertexShaderOutput input) : COLOR
25 | {
26 | float4 texColor = tex2D(SpriteTextureSampler,input.TextureCoordinates) * input.Color;
27 | float gray = (texColor.r + texColor.g + texColor.b) / 3.0;
28 | return float4(gray, gray, gray, texColor.a);
29 | }
30 |
31 | technique SpriteDrawing
32 | {
33 | pass P0
34 | {
35 | PixelShader = compile PS_SHADERMODEL MainPS();
36 | }
37 | };
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Game1.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Xna.Framework;
2 | using Microsoft.Xna.Framework.Graphics;
3 | using Microsoft.Xna.Framework.Input;
4 |
5 |
6 | namespace Iguina.Demo.MonoGame
7 | {
8 | public class Game1 : Game
9 | {
10 | private GraphicsDeviceManager _graphics = null!;
11 | private SpriteBatch _spriteBatch = null!;
12 | MonoGameRenderer _renderer = null!;
13 | MonoGameInput _input = null!;
14 | IguinaDemoStarter _demo = null!;
15 |
16 | public Game1()
17 | {
18 | _graphics = new GraphicsDeviceManager(this);
19 | Content.RootDirectory = "Content";
20 | IsMouseVisible = true;
21 | Window.Title = "Iguina Demo - MonoGame";
22 | }
23 |
24 | protected override void Initialize()
25 | {
26 | base.Initialize();
27 | }
28 |
29 | protected override void LoadContent()
30 | {
31 | _spriteBatch = new SpriteBatch(GraphicsDevice);
32 |
33 | // start demo project and provide our renderer and input provider.
34 | var uiThemeFolder = "../../../../Iguina.Demo/Assets/DefaultTheme";
35 |
36 | // create demo
37 | _demo = new IguinaDemoStarter();
38 | _renderer = new MonoGameRenderer(Content, GraphicsDevice, _spriteBatch, uiThemeFolder);
39 | _input = new MonoGameInput();
40 | _demo.Start(_renderer, _input, uiThemeFolder);
41 |
42 | // set maximized
43 | {
44 | int _ScreenWidth = GraphicsDevice.Adapter.CurrentDisplayMode.Width;
45 | int _ScreenHeight = GraphicsDevice.Adapter.CurrentDisplayMode.Height;
46 | _graphics.PreferredBackBufferWidth = (int)_ScreenWidth;
47 | _graphics.PreferredBackBufferHeight = (int)_ScreenHeight;
48 | IsMouseVisible = false;
49 | Window.AllowUserResizing = true;
50 | _graphics.IsFullScreen = false;
51 | _graphics.ApplyChanges();
52 | Window.AllowUserResizing = true;
53 | Window.IsBorderless = false;
54 | Window.Position = new Point(0, 0);
55 | }
56 | }
57 |
58 | protected override void Update(GameTime gameTime)
59 | {
60 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
61 | Exit();
62 |
63 | _input.StartFrame(gameTime);
64 | _demo.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
65 | _input.EndFrame();
66 |
67 | base.Update(gameTime);
68 | }
69 |
70 | protected override void Draw(GameTime gameTime)
71 | {
72 | GraphicsDevice.Clear(Color.CornflowerBlue);
73 |
74 | _renderer.StartFrame();
75 | _demo.Draw();
76 | _renderer.EndFrame();
77 |
78 | base.Draw(gameTime);
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Icon.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/Iguina.Demo.MonoGame/Icon.bmp
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/Iguina.Demo.MonoGame/Icon.ico
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Iguina.Demo.MonoGame.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net8.0
5 | Major
6 | false
7 | false
8 |
9 |
10 | app.manifest
11 | Icon.ico
12 | enable
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/MonoGameInput.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Drivers;
3 | using Microsoft.Xna.Framework.Input;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | namespace Iguina.Demo.MonoGame
9 | {
10 | ///
11 | /// Provide input for GUI.
12 | ///
13 | internal class MonoGameInput : Drivers.IInputProvider
14 | {
15 | int _lastWheelValue;
16 |
17 | private List _textInput = new();
18 | private const double InitialDelay = 0.45; // Initial delay before repeating input
19 | private const double RepeatRate = 0.045; // Rate of repeated input
20 | private double[] _charsDelay = new double[255];
21 | Keys[] _lastPressedKeys = new Keys[0];
22 |
23 | public void StartFrame(Microsoft.Xna.Framework.GameTime gameTime)
24 | {
25 | var keyboardState = Keyboard.GetState();
26 | var keysPressed = keyboardState.GetPressedKeys();
27 |
28 | // get keyboard text input
29 | {
30 | HashSet _lastKeys = new(_lastPressedKeys);
31 | _textInput.Clear();
32 | var currSeconds = gameTime.TotalGameTime.TotalSeconds;
33 |
34 | foreach (var key in keysPressed)
35 | {
36 | char keyChar = ConvertKeyToChar(key, keyboardState);
37 | int keyCharForDelay = (int)keyChar.ToString().ToLower()[0];
38 | if (keyChar != '\0')
39 | {
40 | if ((currSeconds > _charsDelay[keyCharForDelay]) || (!_lastPressedKeys.Contains(key)))
41 | {
42 | _textInput.Add(keyChar);
43 | _charsDelay[keyCharForDelay] = currSeconds + (_lastKeys.Contains(key) ? RepeatRate : InitialDelay);
44 | }
45 | }
46 | }
47 | _lastPressedKeys = keysPressed;
48 | }
49 | }
50 |
51 | public void EndFrame()
52 | {
53 | var mouse = Mouse.GetState();
54 | _lastWheelValue = mouse.ScrollWheelValue;
55 | }
56 |
57 | public Point GetMousePosition()
58 | {
59 | var mouse =Mouse.GetState();
60 | return new Point(mouse.X, mouse.Y);
61 | }
62 |
63 | public bool IsMouseButtonDown(MouseButton btn)
64 | {
65 | var mouse =Mouse.GetState();
66 | switch (btn)
67 | {
68 | case MouseButton.Left: return mouse.LeftButton == ButtonState.Pressed;
69 | case MouseButton.Right: return mouse.RightButton == ButtonState.Pressed;
70 | case MouseButton.Wheel: return mouse.MiddleButton == ButtonState.Pressed;
71 | }
72 | return false;
73 | }
74 |
75 | public int GetMouseWheelChange()
76 | {
77 | var mouse =Mouse.GetState();
78 | return Math.Sign(mouse.ScrollWheelValue - _lastWheelValue);
79 | }
80 |
81 |
82 | public int[] GetTextInput()
83 | {
84 | return _textInput.ToArray();
85 | }
86 |
87 |
88 | private char ConvertKeyToChar(Keys key, KeyboardState state)
89 | {
90 | bool shift = state.IsKeyDown(Keys.LeftShift) || state.IsKeyDown(Keys.RightShift);
91 | bool capsLock = state.CapsLock;
92 |
93 | switch (key)
94 | {
95 | case Keys.A: return shift ^ capsLock ? 'A' : 'a';
96 | case Keys.B: return shift ^ capsLock ? 'B' : 'b';
97 | case Keys.C: return shift ^ capsLock ? 'C' : 'c';
98 | case Keys.D: return shift ^ capsLock ? 'D' : 'd';
99 | case Keys.E: return shift ^ capsLock ? 'E' : 'e';
100 | case Keys.F: return shift ^ capsLock ? 'F' : 'f';
101 | case Keys.G: return shift ^ capsLock ? 'G' : 'g';
102 | case Keys.H: return shift ^ capsLock ? 'H' : 'h';
103 | case Keys.I: return shift ^ capsLock ? 'I' : 'i';
104 | case Keys.J: return shift ^ capsLock ? 'J' : 'j';
105 | case Keys.K: return shift ^ capsLock ? 'K' : 'k';
106 | case Keys.L: return shift ^ capsLock ? 'L' : 'l';
107 | case Keys.M: return shift ^ capsLock ? 'M' : 'm';
108 | case Keys.N: return shift ^ capsLock ? 'N' : 'n';
109 | case Keys.O: return shift ^ capsLock ? 'O' : 'o';
110 | case Keys.P: return shift ^ capsLock ? 'P' : 'p';
111 | case Keys.Q: return shift ^ capsLock ? 'Q' : 'q';
112 | case Keys.R: return shift ^ capsLock ? 'R' : 'r';
113 | case Keys.S: return shift ^ capsLock ? 'S' : 's';
114 | case Keys.T: return shift ^ capsLock ? 'T' : 't';
115 | case Keys.U: return shift ^ capsLock ? 'U' : 'u';
116 | case Keys.V: return shift ^ capsLock ? 'V' : 'v';
117 | case Keys.W: return shift ^ capsLock ? 'W' : 'w';
118 | case Keys.X: return shift ^ capsLock ? 'X' : 'x';
119 | case Keys.Y: return shift ^ capsLock ? 'Y' : 'y';
120 | case Keys.Z: return shift ^ capsLock ? 'Z' : 'z';
121 | case Keys.D0: return shift ? ')' : '0';
122 | case Keys.D1: return shift ? '!' : '1';
123 | case Keys.D2: return shift ? '@' : '2';
124 | case Keys.D3: return shift ? '#' : '3';
125 | case Keys.D4: return shift ? '$' : '4';
126 | case Keys.D5: return shift ? '%' : '5';
127 | case Keys.D6: return shift ? '^' : '6';
128 | case Keys.D7: return shift ? '&' : '7';
129 | case Keys.D8: return shift ? '*' : '8';
130 | case Keys.D9: return shift ? '(' : '9';
131 | case Keys.Space: return ' ';
132 | case Keys.OemPeriod: return shift ? '>' : '.';
133 | case Keys.OemComma: return shift ? '<' : ',';
134 | case Keys.OemQuestion: return shift ? '?' : '/';
135 | case Keys.OemSemicolon: return shift ? ':' : ';';
136 | case Keys.OemQuotes: return shift ? '"' : '\'';
137 | case Keys.OemBackslash: return shift ? '|' : '\\';
138 | case Keys.OemOpenBrackets: return shift ? '{' : '[';
139 | case Keys.OemCloseBrackets: return shift ? '}' : ']';
140 | case Keys.OemMinus: return shift ? '_' : '-';
141 | case Keys.OemPlus: return shift ? '+' : '=';
142 | default: return '\0';
143 | }
144 | }
145 |
146 | public TextInputCommands[] GetTextInputCommands()
147 | {
148 | List ret = new();
149 | var keyboard = Keyboard.GetState();
150 | var ctrlDown = keyboard.IsKeyDown(Keys.LeftControl) || keyboard.IsKeyDown(Keys.RightControl);
151 | long millisecondsSinceEpoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
152 | {
153 | foreach (var value in Enum.GetValues(typeof(TextInputCommands)))
154 | {
155 | var key = _inputTextCommandToKeyboardKey[(int)value];
156 | long msPassed = millisecondsSinceEpoch - _timeToAllowNextInputCommand[(int)value];
157 | if (keyboard.IsKeyDown(key))
158 | {
159 | if (msPassed > 0)
160 | {
161 | _timeToAllowNextInputCommand[(int)value] = (millisecondsSinceEpoch + (msPassed >= 250 ? 450 : 45));
162 | var command = (TextInputCommands)value;
163 | if ((command == TextInputCommands.MoveCaretEnd) && !ctrlDown) { continue; }
164 | if ((command == TextInputCommands.MoveCaretEndOfLine) && ctrlDown) { continue; }
165 | if ((command == TextInputCommands.MoveCaretStart) && !ctrlDown) { continue; }
166 | if ((command == TextInputCommands.MoveCaretStartOfLine) && ctrlDown) { continue; }
167 | ret.Add(command);
168 | }
169 | }
170 | else
171 | {
172 | _timeToAllowNextInputCommand[(int)value] = 0;
173 | }
174 | }
175 | }
176 | return ret.ToArray();
177 | }
178 |
179 | // to add rate delay and ray limit to input commands
180 | long[] _timeToAllowNextInputCommand = new long[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
181 |
182 | // convert text input command to keyboard key
183 | /*
184 | * enum values:
185 | MoveCaretLeft,
186 | MoveCaretRight,
187 | MoveCaretUp,
188 | MoveCaretDown,
189 | Backspace,
190 | Delete,
191 | End,
192 | Start,
193 | EndOfLine,
194 | StartOfLine
195 | */
196 | static Keys[] _inputTextCommandToKeyboardKey = new Keys[]
197 | {
198 | Keys.Left,
199 | Keys.Right,
200 | Keys.Up,
201 | Keys.Down,
202 | Keys.Back,
203 | Keys.Delete,
204 | Keys.Enter,
205 | Keys.End,
206 | Keys.Home,
207 | Keys.End,
208 | Keys.Home
209 | };
210 |
211 | public KeyboardInteractions? GetKeyboardInteraction()
212 | {
213 | var keyboardState = Keyboard.GetState();
214 |
215 | if (keyboardState.IsKeyDown(Keys.Left))
216 | {
217 | return KeyboardInteractions.MoveLeft;
218 | }
219 | if (keyboardState.IsKeyDown(Keys.Right))
220 | {
221 | return KeyboardInteractions.MoveRight;
222 | }
223 | if (keyboardState.IsKeyDown(Keys.Up))
224 | {
225 | return KeyboardInteractions.MoveUp;
226 | }
227 | if (keyboardState.IsKeyDown(Keys.Down))
228 | {
229 | return KeyboardInteractions.MoveDown;
230 | }
231 | if (keyboardState.IsKeyDown(Keys.Space) || keyboardState.IsKeyDown(Keys.Enter))
232 | {
233 | return KeyboardInteractions.Select;
234 | }
235 | return null;
236 | }
237 | }
238 | }
239 |
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/Program.cs:
--------------------------------------------------------------------------------
1 |
2 | using var game = new Iguina.Demo.MonoGame.Game1();
3 | game.Run();
4 |
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/ReadmeExample.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Entities;
3 | using Microsoft.Xna.Framework;
4 | using Microsoft.Xna.Framework.Graphics;
5 | using Microsoft.Xna.Framework.Input;
6 | using System.IO;
7 |
8 | namespace Iguina.Demo.MonoGame
9 | {
10 | ///
11 | /// Basic monogame example.
12 | ///
13 | public class IguinaMonoGameExample : Game
14 | {
15 | private GraphicsDeviceManager _graphics = null!;
16 | private SpriteBatch _spriteBatch = null!;
17 | UISystem _uiSystem = null!;
18 |
19 | public IguinaMonoGameExample()
20 | {
21 | _graphics = new GraphicsDeviceManager(this);
22 | Content.RootDirectory = "Content";
23 | IsMouseVisible = true;
24 | Window.Title = "Iguina Demo - MonoGame";
25 | }
26 |
27 | protected override void Initialize()
28 | {
29 | base.Initialize();
30 | }
31 |
32 | protected override void LoadContent()
33 | {
34 | _spriteBatch = new SpriteBatch(GraphicsDevice);
35 |
36 | // start demo project and provide our renderer and input provider.
37 | var uiThemeFolder = "../../../../Iguina.Demo/Assets/DefaultTheme";
38 |
39 | // create ui system
40 | var renderer = new MonoGameRenderer(Content, GraphicsDevice, _spriteBatch, uiThemeFolder);
41 | var input = new MonoGameInput();
42 | _uiSystem = new UISystem(Path.Combine(uiThemeFolder, "system_style.json"), renderer, input);
43 |
44 | // create panel with hello message
45 | {
46 | var panel = new Panel(_uiSystem);
47 | panel.Anchor = Defs.Anchor.Center;
48 | panel.Size.SetPixels(400, 400);
49 | _uiSystem.Root.AddChild(panel);
50 |
51 | var paragraph = new Paragraph(_uiSystem);
52 | paragraph.Text = "Hello World!";
53 | panel.AddChild(paragraph);
54 | }
55 | }
56 |
57 | protected override void Update(GameTime gameTime)
58 | {
59 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
60 | Exit();
61 |
62 | // update input and ui system
63 | var input = (_uiSystem.Input as MonoGameInput)!;
64 | input.StartFrame(gameTime);
65 | _uiSystem.Update((float)gameTime.ElapsedGameTime.TotalSeconds);
66 | input.EndFrame();
67 |
68 | base.Update(gameTime);
69 | }
70 |
71 | protected override void Draw(GameTime gameTime)
72 | {
73 | GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.CornflowerBlue);
74 |
75 | // render ui
76 | var renderer = (_uiSystem.Renderer as MonoGameRenderer)!;
77 | renderer.StartFrame();
78 | _uiSystem.Draw();
79 | renderer.EndFrame();
80 |
81 | base.Draw(gameTime);
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/Iguina.Demo.MonoGame/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 | permonitorv2,permonitor
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Iguina.Demo.RayLib/Iguina.Demo.RayLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Iguina.Demo.RayLib/Program.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Demo;
2 | using Iguina.Demo.RayLib;
3 | using Raylib_cs;
4 | using static Raylib_cs.Raylib;
5 |
6 | // uncomment this to play the example from the readme.md file
7 | //ReadmeDemo.Start();
8 |
9 | // get screen resolution for demo size
10 | int screenWidth = Raylib_cs.Raylib.GetScreenWidth();
11 | int screenHeight = Raylib_cs.Raylib.GetScreenHeight();
12 |
13 | // init window
14 | Raylib.SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.ResizableWindow);
15 | InitWindow(screenWidth, screenHeight, "Iguina Demo - RayLib");
16 | //SetWindowState(ConfigFlags.BorderlessWindowMode);
17 | MaximizeWindow();
18 | SetTargetFPS(0);
19 |
20 | // start demo project and provide our renderer and input provider.
21 | var uiThemeFolder = "../../../../Iguina.Demo/Assets/DefaultTheme";
22 | var demo = new IguinaDemoStarter();
23 | var renderer = new RayLibRenderer(uiThemeFolder);
24 | var input = new RayLibInput();
25 | demo.Start(renderer, input, uiThemeFolder);
26 |
27 | // Main game loop
28 | while (!WindowShouldClose())
29 | {
30 | // begin drawing
31 | BeginDrawing();
32 | ClearBackground(Color.DarkBlue);
33 |
34 | // show / hide cursor
35 | if (IsWindowFocused()) { HideCursor(); }
36 | else { ShowCursor(); }
37 |
38 | // update and draw ui
39 | BeginMode2D(new Camera2D() { Zoom = 1f });
40 | renderer.StartFrame();
41 | demo.Update(GetFrameTime());
42 | demo.Draw();
43 | renderer.EndFrame();
44 | EndMode2D();
45 |
46 | // end drawing
47 | EndDrawing();
48 | }
49 |
50 | CloseWindow();
51 | Environment.Exit(0);
--------------------------------------------------------------------------------
/Iguina.Demo.RayLib/RayLibInput.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Drivers;
3 |
4 |
5 | namespace Iguina.Demo.RayLib
6 | {
7 |
8 | ///
9 | /// Provide input for the GUI system.
10 | ///
11 | public class RayLibInput : IInputProvider
12 | {
13 | public Point GetMousePosition()
14 | {
15 | var cp = Raylib_cs.Raylib.GetMousePosition();
16 | return new Point((int)cp.X, (int)cp.Y);
17 | }
18 |
19 | public bool IsMouseButtonDown(MouseButton btn)
20 | {
21 | switch (btn)
22 | {
23 | case MouseButton.Left:
24 | return Raylib_cs.Raylib.IsMouseButtonDown(Raylib_cs.MouseButton.Left);
25 |
26 | case MouseButton.Right:
27 | return Raylib_cs.Raylib.IsMouseButtonDown(Raylib_cs.MouseButton.Right);
28 |
29 | case MouseButton.Wheel:
30 | return Raylib_cs.Raylib.IsMouseButtonDown(Raylib_cs.MouseButton.Middle);
31 | }
32 | return false;
33 | }
34 |
35 | public int GetMouseWheelChange()
36 | {
37 | float val = Raylib_cs.Raylib.GetMouseWheelMove();
38 | return MathF.Sign(val);
39 | }
40 |
41 | public int[] GetTextInput()
42 | {
43 | List ret = new();
44 | while (true)
45 | {
46 | var curr = Raylib_cs.Raylib.GetCharPressed();
47 | if (curr != 0) { ret.Add(curr); }
48 | else { break; }
49 | }
50 | return ret.ToArray();
51 | }
52 |
53 | public TextInputCommands[] GetTextInputCommands()
54 | {
55 | var ctrlDown = Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.LeftControl) || Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Right);
56 |
57 | List ret = new();
58 | long millisecondsSinceEpoch = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
59 | {
60 | foreach (var value in Enum.GetValues(typeof(TextInputCommands)))
61 | {
62 | var key = _inputTextCommandToKeyboardKey[(int)value];
63 | long msPassed = millisecondsSinceEpoch - _timeToAllowNextInputCommand[(int)value];
64 | if (Raylib_cs.Raylib.IsKeyDown(key))
65 | {
66 | if (msPassed > 0)
67 | {
68 | _timeToAllowNextInputCommand[(int)value] = (millisecondsSinceEpoch + (msPassed >= 250 ? 450 : 45));
69 | var command = (TextInputCommands)value;
70 | if ((command == TextInputCommands.MoveCaretEnd) && !ctrlDown) { continue; }
71 | if ((command == TextInputCommands.MoveCaretEndOfLine) && ctrlDown) { continue; }
72 | if ((command == TextInputCommands.MoveCaretStart) && !ctrlDown) { continue; }
73 | if ((command == TextInputCommands.MoveCaretStartOfLine) && ctrlDown) { continue; }
74 | ret.Add(command);
75 | }
76 | }
77 | else
78 | {
79 | _timeToAllowNextInputCommand[(int)value] = 0;
80 | }
81 | }
82 | }
83 | return ret.ToArray();
84 | }
85 |
86 | // to add rate delay and ray limit to input commands
87 | long[] _timeToAllowNextInputCommand = new long[] { 0,0,0,0,0,0,0,0,0,0,0 };
88 |
89 | // convert text input command to keyboard key
90 | /*
91 | * enum values:
92 | MoveCaretLeft,
93 | MoveCaretRight,
94 | MoveCaretUp,
95 | MoveCaretDown,
96 | Backspace,
97 | Delete,
98 | End,
99 | Start,
100 | EndOfLine,
101 | StartOfLine
102 | */
103 | static Raylib_cs.KeyboardKey[] _inputTextCommandToKeyboardKey = new Raylib_cs.KeyboardKey[]
104 | {
105 | Raylib_cs.KeyboardKey.Left,
106 | Raylib_cs.KeyboardKey.Right,
107 | Raylib_cs.KeyboardKey.Up,
108 | Raylib_cs.KeyboardKey.Down,
109 | Raylib_cs.KeyboardKey.Backspace,
110 | Raylib_cs.KeyboardKey.Delete,
111 | Raylib_cs.KeyboardKey.Enter,
112 | Raylib_cs.KeyboardKey.End,
113 | Raylib_cs.KeyboardKey.Home,
114 | Raylib_cs.KeyboardKey.End,
115 | Raylib_cs.KeyboardKey.Home
116 | };
117 |
118 | public KeyboardInteractions? GetKeyboardInteraction()
119 | {
120 | if (Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Left))
121 | {
122 | return KeyboardInteractions.MoveLeft;
123 | }
124 | if (Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Right))
125 | {
126 | return KeyboardInteractions.MoveRight;
127 | }
128 | if (Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Up))
129 | {
130 | return KeyboardInteractions.MoveUp;
131 | }
132 | if (Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Down))
133 | {
134 | return KeyboardInteractions.MoveDown;
135 | }
136 | if (Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Space) || Raylib_cs.Raylib.IsKeyDown(Raylib_cs.KeyboardKey.Enter))
137 | {
138 | return KeyboardInteractions.Select;
139 | }
140 | return null;
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/Iguina.Demo.RayLib/RayLibRenderer.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Drivers;
3 | using System.Numerics;
4 |
5 |
6 | namespace Iguina.Demo.RayLib
7 | {
8 | ///
9 | /// Provide rendering for the GUI system.
10 | ///
11 | public class RayLibRenderer : IRenderer
12 | {
13 | // assets path
14 | string _assetsPath;
15 |
16 | ///
17 | /// Create the ray renderer.
18 | ///
19 | /// Root directory to load assets from. Check out the demo project for details.
20 | public RayLibRenderer(string assetsPath)
21 | {
22 | // store textures path
23 | _assetsPath = assetsPath;
24 |
25 | // create black and white effect for disabled entities
26 | {
27 | string grayscaleShader = @"
28 | #version 330
29 | in vec2 fragTexCoord;
30 | in vec4 fragColor;
31 |
32 | uniform sampler2D texture0;
33 | uniform vec4 colDiffuse;
34 |
35 | out vec4 finalColor;
36 |
37 | void main()
38 | {
39 | vec4 texelColor = texture(texture0, fragTexCoord) * colDiffuse * fragColor;
40 | float gray = (texelColor.r + texelColor.g + texelColor.b) / 3;
41 | finalColor = vec4(vec3(gray), texelColor.a);
42 | }
43 | ";
44 | _disabledShader = Raylib_cs.Raylib.LoadShaderFromMemory(null, grayscaleShader);
45 | }
46 |
47 | // get default font
48 | _defaultFont = Raylib_cs.Raylib.GetFontDefault();
49 | }
50 |
51 | // shader to use for disabled effect
52 | Raylib_cs.Shader _disabledShader;
53 |
54 | // last effect id
55 | string? _lastEffect = null!;
56 |
57 | ///
58 | /// Called at the beginning of every frame.
59 | ///
60 | public void StartFrame()
61 | {
62 | _lastEffect = null;
63 | }
64 |
65 | ///
66 | /// Called at the end of every frame.
67 | ///
68 | public void EndFrame()
69 | {
70 | if (_lastEffect != null)
71 | {
72 | Raylib_cs.Raylib.EndShaderMode();
73 | }
74 | }
75 |
76 | ///
77 | /// Set which effect to use.
78 | ///
79 | void SetEffect(string? effect)
80 | {
81 | // effect changed?
82 | if (effect != _lastEffect)
83 | {
84 | // cancel previous shader
85 | if (_lastEffect != null)
86 | {
87 | Raylib_cs.Raylib.EndShaderMode();
88 | }
89 |
90 | // no effect
91 | if (effect == null)
92 | {
93 | }
94 | // set disabled effect
95 | else if (effect == "disabled")
96 | {
97 | Raylib_cs.Raylib.BeginShaderMode(_disabledShader);
98 | }
99 | // undefined effect!
100 | else
101 | {
102 | throw new Exception("Unknown effect identifier: " + effect);
103 | }
104 |
105 | // set last effect
106 | _lastEffect = effect;
107 | }
108 | }
109 |
110 | ///
111 | /// Get texture instance from texture id.
112 | ///
113 | Raylib_cs.Texture2D GetTexture(string textureId)
114 | {
115 | if (_textures.TryGetValue(textureId, out Raylib_cs.Texture2D tex))
116 | {
117 | return tex;
118 | }
119 | _textures[textureId] = Raylib_cs.Raylib.LoadTexture(Path.Combine(_assetsPath, textureId));
120 | return _textures[textureId];
121 | }
122 |
123 | // cached textures
124 | Dictionary _textures = new();
125 |
126 | ///
127 | /// Get font from identifier.
128 | ///
129 | Raylib_cs.Font GetFont(string? fontId)
130 | {
131 | if (fontId == null)
132 | {
133 | return _defaultFont;
134 | }
135 |
136 | if (_fonts.TryGetValue(fontId, out Raylib_cs.Font font))
137 | {
138 | return font;
139 | }
140 | _fonts[fontId] = Raylib_cs.Raylib.LoadFont(Path.Combine(_assetsPath, fontId));
141 | return _fonts[fontId];
142 | }
143 |
144 | // cached fonts
145 | Dictionary _fonts = new();
146 | Raylib_cs.Font _defaultFont;
147 |
148 | ///
149 | public Rectangle GetScreenBounds()
150 | {
151 | return new Rectangle(0, 0, Raylib_cs.Raylib.GetRenderWidth(), Raylib_cs.Raylib.GetRenderHeight());
152 | }
153 |
154 | ///
155 | [Obsolete("Note: currently we render outline in a primitive way. To improve performance and remove some visual artifact during transitions, its best to implement a shader that draw text with outline properly.")]
156 | public void DrawText(string? effectIdentifier, string text, string? fontId, int fontSize, Point position, Color fillColor, Color outlineColor, int outlineWidth, float spacing)
157 | {
158 | // set text effect
159 | SetEffect(effectIdentifier);
160 |
161 | // get font
162 | var font = GetFont(fontId);
163 |
164 | // draw outline
165 | if ((outlineColor.A > 0) && (outlineWidth > 0))
166 | {
167 | // because we draw outline in a primitive way, we want it to fade a lot faster than fill color
168 | if (outlineColor.A < 255)
169 | {
170 | float alphaFactor = (float)(outlineColor.A / 255f);
171 | outlineColor.A = (byte)((float)fillColor.A * Math.Pow(alphaFactor, 7));
172 | }
173 |
174 | // draw outline
175 | var outline = new Raylib_cs.Color(outlineColor.R, outlineColor.G, outlineColor.B, outlineColor.A);
176 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X - outlineWidth, position.Y), fontSize, spacing, outline);
177 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X, position.Y - outlineWidth), fontSize, spacing, outline);
178 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X + outlineWidth, position.Y), fontSize, spacing, outline);
179 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X, position.Y + outlineWidth), fontSize, spacing, outline);
180 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X - outlineWidth, position.Y - outlineWidth), fontSize, spacing, outline);
181 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X - outlineWidth, position.Y + outlineWidth), fontSize, spacing, outline);
182 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X + outlineWidth, position.Y - outlineWidth), fontSize, spacing, outline);
183 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X + outlineWidth, position.Y + outlineWidth), fontSize, spacing, outline);
184 | }
185 |
186 | // draw text
187 | var fill = new Raylib_cs.Color(fillColor.R, fillColor.G, fillColor.B, fillColor.A);
188 | Raylib_cs.Raylib.DrawTextEx(font, text, new System.Numerics.Vector2(position.X, position.Y), fontSize, spacing, fill);
189 | }
190 |
191 | ///
192 | public int GetTextLineHeight(string? fontId, int fontSize)
193 | {
194 | var font = GetFont(fontId);
195 | float scale = fontSize / (float)font.BaseSize;
196 | return (int)(font.BaseSize * scale);
197 | }
198 |
199 | ///
200 | public Point MeasureText(string text, string? fontId, int fontSize, float spacing)
201 | {
202 | var font = GetFont(fontId);
203 | var ret = Raylib_cs.Raylib.MeasureTextEx(font, text, fontSize, spacing);
204 | return new Point((int)ret.X, (int)ret.Y);
205 | }
206 |
207 | ///
208 | public void DrawTexture(string? effectIdentifier, string textureId, Rectangle destRect, Rectangle sourceRect, Color color)
209 | {
210 | // set active effect
211 | SetEffect(effectIdentifier);
212 |
213 | // get texture
214 | var texture = GetTexture(textureId);
215 |
216 | // draw texture
217 | Raylib_cs.Raylib.DrawTexturePro(texture,
218 | new Raylib_cs.Rectangle(sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height),
219 | new Raylib_cs.Rectangle(destRect.X, destRect.Y, destRect.Width, destRect.Height),
220 | new System.Numerics.Vector2(0f, 0f),
221 | 0f,
222 | new Raylib_cs.Color(color.R, color.G, color.B, color.A));
223 | }
224 |
225 | // currently set scissor region
226 | Rectangle? _currentScissorRegion;
227 |
228 | ///
229 | public void SetScissorRegion(Rectangle region)
230 | {
231 | _currentScissorRegion = region;
232 | Raylib_cs.Raylib.BeginScissorMode(region.X, region.Y, region.Width, region.Height);
233 | }
234 |
235 | ///
236 | public void DrawRectangle(Rectangle rectangle, Color color)
237 | {
238 | SetEffect(null);
239 | Raylib_cs.Raylib.DrawRectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, new Raylib_cs.Color(color.R, color.G, color.B, color.A));
240 | }
241 |
242 | ///
243 | public void ClearScissorRegion()
244 | {
245 | _currentScissorRegion = null;
246 | Raylib_cs.Raylib.EndScissorMode();
247 | }
248 |
249 | ///
250 | public Rectangle? GetScissorRegion()
251 | {
252 | return _currentScissorRegion;
253 | }
254 |
255 | ///
256 | /// Get image from texture.
257 | ///
258 | Raylib_cs.Image GetImageFromTexture(string textureId)
259 | {
260 | Raylib_cs.Image image;
261 | var texture = GetTexture(textureId);
262 | if (!_cachedImageData.TryGetValue(textureId, out image))
263 | {
264 | image = Raylib_cs.Raylib.LoadImageFromTexture(texture);
265 | _cachedImageData[textureId] = image;
266 | }
267 | return image;
268 | }
269 | Dictionary _cachedImageData = new();
270 |
271 | ///
272 | public Color GetPixelFromTexture(string textureId, Point sourcePosition)
273 | {
274 | var image = GetImageFromTexture(textureId);
275 | if (sourcePosition.X < 0) sourcePosition.X = 0;
276 | if (sourcePosition.Y < 0) sourcePosition.Y = 0;
277 | if (sourcePosition.X >= image.Width) sourcePosition.X = image.Width - 1;
278 | if (sourcePosition.Y >= image.Height) sourcePosition.Y = image.Height - 1;
279 | var pixelColor = Raylib_cs.Raylib.GetImageColor(image, sourcePosition.X, sourcePosition.Y);
280 | return new Color(pixelColor.R, pixelColor.G, pixelColor.B, pixelColor.A);
281 | }
282 |
283 | ///
284 | public Point? FindPixelOffsetInTexture(string textureId, Rectangle sourceRect, Color color, bool returnNearestColor)
285 | {
286 | Point? ret = null;
287 | float nearestDistance = 255f * 255f * 255f * 255f;
288 | var image = GetImageFromTexture(textureId);
289 | for (int x = 0; x < sourceRect.Width; x++)
290 | {
291 | for (int y = 0; y < sourceRect.Height; y++)
292 | {
293 | var pixelColor = Raylib_cs.Raylib.GetImageColor(image, x + sourceRect.Left, y + sourceRect.Top);
294 | if ((pixelColor.R == color.R) && (pixelColor.G == color.G) && (pixelColor.B == color.B) && (pixelColor.A == color.A))
295 | {
296 | return new Point(x, y);
297 | }
298 | else if (returnNearestColor)
299 | {
300 | float distance = Vector4.DistanceSquared(new Vector4(pixelColor.R, pixelColor.G, pixelColor.B, pixelColor.A), new Vector4(color.R, color.G, color.B, color.A));
301 | if (distance < nearestDistance)
302 | {
303 | nearestDistance = distance;
304 | ret = new Point(x, y);
305 | }
306 | }
307 | }
308 | }
309 | return ret;
310 | }
311 | }
312 | }
313 |
--------------------------------------------------------------------------------
/Iguina.Demo.RayLib/ReadmeExample.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Demo.RayLib;
3 | using Iguina.Entities;
4 | using Raylib_cs;
5 | using static Raylib_cs.Raylib;
6 |
7 | static class ReadmeDemo
8 | {
9 | public static void Start()
10 | {
11 | // get screen resolution for demo size
12 | int screenWidth = 800;
13 | int screenHeight = 600;
14 |
15 | // init window
16 | Raylib.SetConfigFlags(ConfigFlags.Msaa4xHint | ConfigFlags.ResizableWindow);
17 | InitWindow(screenWidth, screenHeight, "Iguina.Demo.RayLib");
18 |
19 | // start demo project and provide our renderer and input provider.
20 | var uiThemeFolder = "../../../../Iguina.Demo/Assets/DefaultTheme";
21 | var renderer = new RayLibRenderer(uiThemeFolder);
22 | var input = new RayLibInput();
23 | var uiSystem = new Iguina.UISystem(Path.Combine(uiThemeFolder, "system_style.json"), renderer, input);
24 |
25 | // create panel with hello message
26 | {
27 | var panel = new Panel(uiSystem);
28 | panel.Anchor = Anchor.Center;
29 | panel.Size.SetPixels(400, 400);
30 | uiSystem.Root.AddChild(panel);
31 |
32 | var paragraph = new Paragraph(uiSystem);
33 | paragraph.Text = "Hello World!";
34 | panel.AddChild(paragraph);
35 | }
36 |
37 | // Main game loop
38 | while (!WindowShouldClose())
39 | {
40 | // begin drawing
41 | BeginDrawing();
42 | ClearBackground(Raylib_cs.Color.DarkBlue);
43 |
44 | // update and draw ui
45 | BeginMode2D(new Camera2D() { Zoom = 1f });
46 | renderer.StartFrame();
47 | uiSystem.Update(GetFrameTime());
48 | uiSystem.Draw();
49 | renderer.EndFrame();
50 | EndMode2D();
51 |
52 | // end drawing
53 | EndDrawing();
54 | }
55 |
56 | CloseWindow();
57 | Environment.Exit(0);
58 | }
59 | }
--------------------------------------------------------------------------------
/Iguina.Demo.RayLib/readme.md:
--------------------------------------------------------------------------------
1 | # Iguina.Demo.RayLib
2 |
3 | RayLib demo project.
4 | This project merely implement the drivers layer with RayLib (renderer + input provider) and use the `Iguina.Demo` project to create an example UI system.
5 |
6 | For full documentation, check out the readme file at the repository root.
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/button.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "FillTextureFramed": {
5 |
6 | "InternalSourceRect": {"X": 116, "Y": 4, "Width": 72, "Height": 24},
7 | "ExternalSourceRect": {"X": 112, "Y": 0, "Width": 80, "Height": 32}
8 | },
9 | "TextAlignment": "Center",
10 | "Padding": {"Left": 8, "Right": 8, "Top": 8, "Bottom": 8}
11 | },
12 | "Targeted": {
13 | "FillTextureFramed": {
14 |
15 | "InternalSourceRect": {"X": 116, "Y": 36, "Width": 72, "Height": 24},
16 | "ExternalSourceRect": {"X": 112, "Y": 32, "Width": 80, "Height": 32}
17 | }
18 | },
19 | "Interacted": {
20 | "FillTextureFramed": {
21 |
22 | "InternalSourceRect": {"X": 116, "Y": 68, "Width": 72, "Height": 24},
23 | "ExternalSourceRect": {"X": 112, "Y": 64, "Width": 80, "Height": 32}
24 | }
25 | },
26 | "TargetedChecked": {
27 | },
28 | "Checked": {
29 | },
30 | "Disabled": {
31 | "EffectIdentifier": "disabled"
32 | },
33 | "DefaultTextAnchor": "Center",
34 | "InterpolateStatesSpeed": 5
35 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/checkbox.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "Icon": {
5 |
6 | "SourceRect": {"X": 80, "Y": 96, "Width": 16, "Height": 16},
7 | "CenterVertically": true
8 | },
9 | "TextAlignment": "Left",
10 | "Padding": {"Left": 20, "Right": 0, "Top": 0, "Bottom": 0}
11 | },
12 | "Targeted": {
13 | "Icon": {
14 | "SourceRect": {"X": 112, "Y": 96, "Width": 16, "Height": 16},
15 | "CenterVertically": true
16 | }
17 | },
18 | "TargetedChecked": {
19 | },
20 | "Interacted": {
21 | "Icon": {
22 |
23 | "SourceRect": {"X": 96, "Y": 96, "Width": 16, "Height": 16},
24 | "CenterVertically": true
25 | }
26 | },
27 | "Checked": {
28 | },
29 | "DefaultTextAnchor": "CenterLeft",
30 | "InterpolateStatesSpeed": 5
31 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/color_picker.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureStretched": {
4 |
5 | "SourceRect": {"X": 128, "Y": 192, "Width": 128, "Height": 32}
6 | },
7 | "Padding": {"Left": 0, "Right": 0, "Top": 0, "Bottom": 0},
8 | "MarginBefore": {"X": 18, "Y": 18},
9 | "MarginAfter": {"X": 18, "Y": 18}
10 | },
11 | "Disabled": {
12 | "EffectIdentifier": "disabled"
13 | },
14 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
15 | "DefaultHeight": {"Value": 96, "Units": "Pixels"},
16 | "InterpolateStatesSpeed": 5,
17 | "InterpolateOffsetsSpeed": 10
18 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/color_picker_handle.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "Icon": {
4 | "SourceRect": {"X": 96, "Y": 192, "Width": 16, "Height": 16}
5 |
6 | }
7 | },
8 | "Disabled": {
9 | "EffectIdentifier": "disabled"
10 | },
11 | "DefaultWidth": {"Value": 32, "Units": "Pixels"},
12 | "DefaultHeight": {"Value": 32, "Units": "Pixels"},
13 | "InterpolateStatesSpeed": 5,
14 | "InterpolateOffsetsSpeed": 10
15 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/color_slider_horizontal.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureStretched": {
4 | "SourceRect": {"X": 128, "Y": 186, "Width": 128, "Height": 6},
5 | "ExtraSize": {"Left": -10, "Right": -10, "Top": 0, "Bottom": 0}
6 | },
7 | "Padding": {"Left": 10, "Right": 10, "Top": 0, "Bottom": 0},
8 | "MarginBefore": {"X": 0, "Y": 18},
9 | "MarginAfter": {"X": 0, "Y": 18}
10 | },
11 | "Disabled": {
12 | "EffectIdentifier": "disabled"
13 | },
14 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
15 | "DefaultHeight": {"Value": 18, "Units": "Pixels"},
16 | "InterpolateStatesSpeed": 5,
17 | "InterpolateOffsetsSpeed": 10
18 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/color_slider_horizontal_handle.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "Icon": {
4 | "SourceRect": {"X": 123, "Y": 144, "Width": 10, "Height": 12},
5 | "CenterVertically": true
6 | }
7 | },
8 | "Targeted": {
9 | "Icon": {
10 | "SourceRect": {"X": 123, "Y": 156, "Width": 10, "Height": 12},
11 | "CenterVertically": true
12 | }
13 | },
14 | "Interacted": {
15 | "Icon": {
16 | "SourceRect": {"X": 123, "Y": 168, "Width": 10, "Height": 12},
17 | "CenterVertically": true
18 | }
19 | },
20 | "Disabled": {
21 | "EffectIdentifier": "disabled"
22 | },
23 | "DefaultWidth": {"Value": 30, "Units": "Pixels"},
24 | "DefaultHeight": {"Value": 36, "Units": "Pixels"},
25 | "InterpolateStatesSpeed": 5,
26 | "InterpolateOffsetsSpeed": 10
27 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/dropdown_icon.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "Icon": {
4 | "SourceRect": {"X": 160, "Y": 96, "Width": 16, "Height": 16}
5 | }
6 | },
7 | "Interacted": {
8 | "Icon": {
9 | "SourceRect": {"X": 176, "Y": 96, "Width": 16, "Height": 16}
10 | }
11 | },
12 | "DefaultAnchor": "TopRight",
13 | "DefaultWidth": {"Value": 32, "Units": "Pixels"},
14 | "DefaultHeight": {"Value": 32, "Units": "Pixels"}
15 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/horizontal_line.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 | "InternalSourceRect": {"X": 3, "Y": 96, "Width": 74, "Height": 2},
5 | "ExternalSourceRect": {"X": 0, "Y": 96, "Width": 80, "Height": 2}
6 |
7 | },
8 | "Padding": {"Left": 8, "Right": 8, "Top": 0, "Bottom": 0},
9 | "MarginBefore": {"X": 0, "Y": 5},
10 | "MarginAfter": {"X": 0, "Y": 6}
11 | },
12 | "Disabled": {
13 | "EffectIdentifier": "disabled"
14 | },
15 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
16 | "DefaultHeight": {"Value": 8, "Units": "Pixels"}
17 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/label.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "TextFillColor": {"R": 220, "G": 220, "B": 220, "A": 255},
5 | "TextScale": 0.85,
6 | "MarginAfter": {"X": 0, "Y": 2}
7 | }
8 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/list_item.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "TextAlignment": "Left",
5 | "DefaultTextAnchor": "CenterLeft",
6 | "ExtraSize": {"Left": 0, "Right": 0, "Top": 0, "Bottom": 10}
7 | },
8 | "Targeted": {
9 | },
10 | "Checked": {
11 | "FillTextureStretched": {
12 |
13 | "SourceRect": {"X": 0, "Y": 80, "Width": 16, "Height": 16},
14 | "ExtraSize": {"Left": 20, "Right": 10, "Top": 10, "Bottom": 0}
15 | },
16 | "TextFillColor": {"R": 0, "G": 255, "B": 255, "A": 255}
17 | },
18 | "InterpolateStatesSpeed": 0
19 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/list_panel.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 4, "Y": 132, "Width": 88, "Height": 88},
6 | "ExternalSourceRect": {"X": 0, "Y": 128, "Width": 96, "Height": 96}
7 | },
8 | "Padding": {"Left": 18, "Right": 18, "Top": 12, "Bottom": 4},
9 | "MarginAfter": {"X": 0, "Y": 6}
10 | },
11 | "Disabled": {
12 | "EffectIdentifier": "disabled"
13 | },
14 | "InterpolateStatesSpeed": 5
15 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/message_box_backdrop.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "BackgroundColor": {
4 | "R": 0,
5 | "G": 0,
6 | "B": 0,
7 | "A": 150
8 | }
9 | },
10 | "Disabled": {
11 | "EffectIdentifier": "disabled"
12 | },
13 | "InterpolateStatesSpeed": 2.5
14 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/numeric_input.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "text_input.json",
3 | "Default": {
4 | "TextAlignment": "Center",
5 | "Padding": {"Left": 2, "Right": 2, "Top": 6, "Bottom": 6}
6 | },
7 | "DefaultTextAnchor": "Center"
8 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/numeric_input_button.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "button.json",
3 | "Default": {
4 | "MarginBefore": {"X": 4, "Y": 0},
5 | "MarginAfter": {"X": 4, "Y": 0}
6 | },
7 | "DefaultWidth": {"Units": "Pixels", "Value": 40},
8 | "DefaultHeight": {"Units": "Pixels", "Value": 40}
9 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/panel.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 | "ExternalSourceRect": {"X": 16, "Y": 0, "Width": 96, "Height": 96},
5 | "FrameWidth": {"X": 4, "Y": 4}
6 | },
7 | "Padding": {"Left": 18, "Right": 18, "Top": 14, "Bottom": 14}
8 | },
9 | "Disabled": {
10 | "EffectIdentifier": "disabled"
11 | },
12 | "InterpolateStatesSpeed": 5
13 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/panel_title.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 4, "Y": 98, "Width": 72, "Height": 14},
6 | "ExternalSourceRect": {"X": 0, "Y": 98, "Width": 80, "Height": 14},
7 | "Offset": {"X": 0, "Y": 0}
8 | },
9 | "Padding": {"Left": 6, "Right": 6, "Top": 0, "Bottom": 0},
10 | "ExtraSize": {"Left": 60, "Right": 60, "Top": 0, "Bottom": 0},
11 | "TextFillColor": {"R": 0, "G": 255, "B": 255, "A": 255},
12 | "TextOutlineColor": {"R": 0, "G": 0, "B": 0, "A": 255},
13 | "MarginAfter": {"X": 0, "Y": 20},
14 | "FontSize": 22,
15 | "TextSpacing": 1.75,
16 | "TextOutlineWidth": 2,
17 | "TextAlignment": "Center"
18 | },
19 | "Disabled": {
20 | "EffectIdentifier": "disabled"
21 | }
22 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/paragraph.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "MarginAfter": {"X": 0, "Y": 6}
5 | }
6 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/paragraph_base_style.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "TextFillColor": {"R": 255, "G": 255, "B": 255, "A": 255},
4 | "TextOutlineColor": {"R": 0, "G": 0, "B": 0, "A": 255},
5 | "NoValueTextFillColor": {"R": 175, "G": 175, "B": 175, "A": 255},
6 | "FontSize": 22,
7 | "TextSpacing": 2,
8 | "TextOutlineWidth": 2,
9 | "TextAlignment": "Left"
10 | },
11 | "Targeted": {
12 | "TextFillColor": {"R": 0, "G": 255, "B": 255, "A": 255}
13 | },
14 | "Interacted": {
15 | "TextFillColor": {"R": 0, "G": 255, "B": 255, "A": 255}
16 | },
17 | "TargetedChecked": {
18 | "TextFillColor": {"R": 0, "G": 255, "B": 255, "A": 255}
19 | },
20 | "Checked": {
21 | "TextFillColor": {"R": 255, "G": 255, "B": 255, "A": 255}
22 | },
23 | "Disabled": {
24 | "EffectIdentifier": "disabled"
25 | }
26 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/progress_bar_horizontal.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 101, "Y": 130, "Width": 70, "Height": 12},
6 | "ExternalSourceRect": {"X": 96, "Y": 128, "Width": 80, "Height": 16}
7 | },
8 | "Padding": {"Left": 2, "Right": 2, "Top": 0, "Bottom": 0},
9 | "MarginBefore": {"X": 0, "Y": 18},
10 | "MarginAfter": {"X": 0, "Y": 18}
11 | },
12 | "Disabled": {
13 | "EffectIdentifier": "disabled"
14 | },
15 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
16 | "DefaultHeight": {"Value": 32, "Units": "Pixels"},
17 | "InterpolateStatesSpeed": 5,
18 | "InterpolateOffsetsSpeed": 10
19 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/progress_bar_horizontal_alt.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 102, "Y": 144, "Width": 14, "Height": 12},
6 | "ExternalSourceRect": {"X": 96, "Y": 144, "Width": 26, "Height": 12}
7 | },
8 | "Padding": {"Left": 0, "Right": 0, "Top": 0, "Bottom": 0},
9 | "MarginBefore": {"X": 0, "Y": 18},
10 | "MarginAfter": {"X": 0, "Y": 18}
11 | },
12 | "Targeted": {
13 | "FillTextureFramed": {
14 |
15 | "InternalSourceRect": {"X": 102, "Y": 156, "Width": 14, "Height": 12},
16 | "ExternalSourceRect": {"X": 96, "Y": 156, "Width": 26, "Height": 12}
17 | }
18 | },
19 | "Disabled": {
20 | "EffectIdentifier": "disabled"
21 | },
22 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
23 | "DefaultHeight": {"Value": 32, "Units": "Pixels"},
24 | "InterpolateStatesSpeed": 5
25 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/progress_bar_horizontal_alt_fill.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 102, "Y": 168, "Width": 14, "Height": 12},
6 | "ExternalSourceRect": {"X": 96, "Y": 168, "Width": 26, "Height": 12}
7 | },
8 | "Padding": {"Left": 0, "Right": 0, "Top": 0, "Bottom": 0},
9 | "MarginBefore": {"X": 0, "Y": 18},
10 | "MarginAfter": {"X": 0, "Y": 18}
11 | },
12 | "Disabled": {
13 | "EffectIdentifier": "disabled"
14 | },
15 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
16 | "DefaultHeight": {"Value": 32, "Units": "Pixels"},
17 | "InterpolateStatesSpeed": 0,
18 | "InterpolateOffsetsSpeed": 0,
19 | "MinWidth": 30,
20 | "DefaultAnchor": "CenterLeft"
21 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/progress_bar_horizontal_fill.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 133, "Y": 114, "Width": 70, "Height": 12},
6 | "ExternalSourceRect": {"X": 128, "Y": 112, "Width": 80, "Height": 16}
7 | },
8 | "Padding": {"Left": 40, "Right": 40, "Top": 0, "Bottom": 0},
9 | "MarginBefore": {"X": 0, "Y": 18},
10 | "MarginAfter": {"X": 0, "Y": 18},
11 | "TintColor": {"R": 155, "G": 255, "B": 255, "A": 255}
12 | },
13 | "Disabled": {
14 | "EffectIdentifier": "disabled"
15 | },
16 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
17 | "DefaultHeight": {"Value": 32, "Units": "Pixels"},
18 | "InterpolateStatesSpeed": 5,
19 | "InterpolateOffsetsSpeed": 10,
20 | "MinWidth": 30,
21 | "DefaultAnchor": "CenterLeft"
22 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/radio_button.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "checkbox.json",
3 | "Default": {
4 | "Icon": {
5 |
6 | "SourceRect": {"X": 80, "Y": 112, "Width": 16, "Height": 16},
7 | "CenterVertically": true
8 | }
9 | },
10 | "Targeted": {
11 | "Icon": {
12 |
13 | "SourceRect": {"X": 112, "Y": 112, "Width": 16, "Height": 16},
14 | "CenterVertically": true
15 | }
16 | },
17 | "Interacted": {
18 | "Icon": {
19 |
20 | "SourceRect": {"X": 96, "Y": 112, "Width": 16, "Height": 16},
21 | "CenterVertically": true
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/scrollbar_vertical.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 192, "Y": 12, "Width": 16, "Height": 72},
6 | "ExternalSourceRect": {"X": 192, "Y": 0, "Width": 16, "Height": 96}
7 | },
8 | "Padding": {"Left": 0, "Right": 0, "Top": 70, "Bottom": 70},
9 | "MarginBefore": {"X": 10, "Y": 10},
10 | "MarginAfter": {"X": 10, "Y": 10}
11 | },
12 | "Disabled": {
13 | "EffectIdentifier": "disabled"
14 | },
15 | "DefaultWidth": {"Value": 32, "Units": "Pixels"},
16 | "DefaultHeight": {"Value": 100, "Units": "PercentOfParent"},
17 | "InterpolateStatesSpeed": 5,
18 | "InterpolateOffsetsSpeed": 10
19 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/scrollbar_vertical_handle.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "ExternalSourceRect": {"X": 208, "Y": 0, "Width": 16, "Height": 48},
6 | "InternalSourceRect": {"X": 208, "Y": 12, "Width": 16, "Height": 24}
7 | }
8 | },
9 | "Targeted": {
10 | "FillTextureFramed": {
11 |
12 | "ExternalSourceRect": {"X": 240, "Y": 0, "Width": 16, "Height": 48},
13 | "InternalSourceRect": {"X": 240, "Y": 12, "Width": 16, "Height": 24}
14 | }
15 | },
16 | "Interacted": {
17 | "FillTextureFramed": {
18 |
19 | "ExternalSourceRect": {"X": 240, "Y": 0, "Width": 16, "Height": 48},
20 | "InternalSourceRect": {"X": 240, "Y": 12, "Width": 16, "Height": 24}
21 | }
22 | },
23 | "Disabled": {
24 | "EffectIdentifier": "disabled"
25 | },
26 | "DefaultWidth": {"Value": 32, "Units": "Pixels"},
27 | "DefaultHeight": {"Value": 80, "Units": "Pixels"},
28 | "InterpolateStatesSpeed": 5,
29 | "InterpolateOffsetsSpeed": 10
30 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/slider_handle.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "Icon": {
4 |
5 | "SourceRect": {"X": 139, "Y": 96, "Width": 11, "Height": 11}
6 | }
7 | },
8 | "Interacted": {
9 | "Icon": {
10 |
11 | "SourceRect": {"X": 128, "Y": 96, "Width": 11, "Height": 11}
12 | }
13 | },
14 | "Disabled": {
15 | "EffectIdentifier": "disabled"
16 | },
17 | "DefaultWidth": {"Value": 22, "Units": "Pixels"},
18 | "DefaultHeight": {"Value": 22, "Units": "Pixels"},
19 | "InterpolateStatesSpeed": 5,
20 | "InterpolateOffsetsSpeed": 10
21 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/slider_horizontal.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 5, "Y": 112, "Width": 70, "Height": 7},
6 | "ExternalSourceRect": {"X": 0, "Y": 112, "Width": 80, "Height": 7}
7 | },
8 | "Padding": {"Left": 40, "Right": 40, "Top": 0, "Bottom": 0},
9 | "MarginBefore": {"X": 0, "Y": 18},
10 | "MarginAfter": {"X": 0, "Y": 18}
11 | },
12 | "Targeted": {
13 | "FillTextureFramed": {
14 |
15 | "InternalSourceRect": {"X": 5, "Y": 119, "Width": 70, "Height": 7},
16 | "ExternalSourceRect": {"X": 0, "Y": 119, "Width": 80, "Height": 7}
17 | }
18 | },
19 | "Interacted": {
20 | "FillTextureFramed": {
21 |
22 | "InternalSourceRect": {"X": 5, "Y": 119, "Width": 70, "Height": 7},
23 | "ExternalSourceRect": {"X": 0, "Y": 119, "Width": 80, "Height": 7}
24 | }
25 | },
26 | "Disabled": {
27 | "EffectIdentifier": "disabled"
28 | },
29 | "DefaultWidth": {"Value": 100, "Units": "PercentOfParent"},
30 | "DefaultHeight": {"Value": 14, "Units": "Pixels"},
31 | "InterpolateStatesSpeed": 5,
32 | "InterpolateOffsetsSpeed": 10
33 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/slider_vertical.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 |
5 | "InternalSourceRect": {"X": 215, "Y": 53, "Width": 7, "Height": 70},
6 | "ExternalSourceRect": {"X": 215, "Y": 48, "Width": 7, "Height": 80}
7 | },
8 | "Padding": {"Left": 0, "Right": 0, "Top": 40, "Bottom": 40},
9 | "MarginBefore": {"X": 18, "Y": 10},
10 | "MarginAfter": {"X": 18, "Y": 10}
11 | },
12 | "Targeted": {
13 | "FillTextureFramed": {
14 |
15 | "InternalSourceRect": {"X": 208, "Y": 53, "Width": 7, "Height": 70},
16 | "ExternalSourceRect": {"X": 208, "Y": 48, "Width": 7, "Height": 80}
17 | }
18 | },
19 | "Interacted": {
20 | "FillTextureFramed": {
21 |
22 | "InternalSourceRect": {"X": 208, "Y": 53, "Width": 7, "Height": 70},
23 | "ExternalSourceRect": {"X": 208, "Y": 48, "Width": 7, "Height": 80}
24 | }
25 | },
26 | "Disabled": {
27 | "EffectIdentifier": "disabled"
28 | },
29 | "DefaultWidth": {"Value": 14, "Units": "Pixels"},
30 | "DefaultHeight": {"Value": 100, "Units": "PercentOfParent"},
31 | "InterpolateStatesSpeed": 5,
32 | "InterpolateOffsetsSpeed": 10
33 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/text_input.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "FillTextureFramed": {
5 |
6 | "InternalSourceRect": {"X": 4, "Y": 132, "Width": 88, "Height": 88},
7 | "ExternalSourceRect": {"X": 0, "Y": 128, "Width": 96, "Height": 96}
8 | },
9 | "Padding": {"Left": 6, "Right": 6, "Top": 6, "Bottom": 6}
10 | },
11 | "Targeted": {
12 | "TextFillColor": {"R": 255, "G": 255, "B": 255, "A": 255}
13 | },
14 | "Interacted": {
15 | "TextFillColor": {"R": 255, "G": 255, "B": 255, "A": 255}
16 | },
17 | "DefaultHeight": {"Value": 48, "Units": "Pixels"},
18 | "InterpolateStatesSpeed": 5
19 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/title.json:
--------------------------------------------------------------------------------
1 | {
2 | "InheritFrom": "paragraph_base_style.json",
3 | "Default": {
4 | "TextFillColor": {"R": 0, "G": 255, "B": 255, "A": 255},
5 | "TextScale": 1.2,
6 | "TextOutlineWidth": 3,
7 | "TextSpacing": 3,
8 | "TextAlignment": "Center"
9 | },
10 | "DefaultTextAnchor": "AutoCenter"
11 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Styles/vertical_line.json:
--------------------------------------------------------------------------------
1 | {
2 | "Default": {
3 | "FillTextureFramed": {
4 | "InternalSourceRect": {"X": 222, "Y": 51, "Width": 2, "Height": 74},
5 | "ExternalSourceRect": {"X": 222, "Y": 48, "Width": 2, "Height": 80}
6 | },
7 | "Padding": {"Left": 0, "Right": 0, "Top": 8, "Bottom": 8},
8 | "MarginBefore": {"X": 6, "Y": 0},
9 | "MarginAfter": {"X": 6, "Y": 0}
10 | },
11 | "Disabled": {
12 | "EffectIdentifier": "disabled"
13 | },
14 | "DefaultWidth": {"Value": 8, "Units": "Pixels"},
15 | "DefaultHeight": {"Value": 100, "Units": "PercentOfParent"}
16 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Textures/Icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/Iguina.Demo/Assets/DefaultTheme/Textures/Icons.png
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/Textures/UI.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/Iguina.Demo/Assets/DefaultTheme/Textures/UI.png
--------------------------------------------------------------------------------
/Iguina.Demo/Assets/DefaultTheme/system_style.json:
--------------------------------------------------------------------------------
1 | {
2 | "ThemeIdentifier": "Default UI",
3 | "CursorDefault": {
4 |
5 | "SourceRect": {"X": 0, "Y": 0, "Width": 16, "Height": 16}
6 | },
7 | "CursorDisabled": {
8 |
9 | "SourceRect": {"X": 0, "Y": 16, "Width": 16, "Height": 16}
10 | },
11 | "CursorLocked": {
12 |
13 | "SourceRect": {"X": 0, "Y": 16, "Width": 16, "Height": 16}
14 | },
15 | "CursorInteractable": {
16 |
17 | "SourceRect": {"X": 0, "Y": 32, "Width": 16, "Height": 16}
18 | },
19 | "FocusedEntityOverlay": {
20 |
21 | "InternalSourceRect": {"X": 177, "Y": 129, "Width": 30, "Height": 30},
22 | "ExternalSourceRect": {"X": 176, "Y": 128, "Width": 32, "Height": 32}
23 | },
24 | "RowSpaceHeight": 14,
25 | "DefaultTexture": "Textures/UI.png",
26 | "TextScale": 1,
27 | "TextureScale": 2,
28 | "CursorScale": 1,
29 | "TimeToLockInteractiveState": 0.135,
30 | "SystemIcons": {
31 | "file": {
32 |
33 | "SourceRect": {"X": 240, "Y": 48, "Width": 16, "Height": 16}
34 | },
35 | "folder": {
36 |
37 | "SourceRect": {"X": 240, "Y": 64, "Width": 16, "Height": 16}
38 | }
39 | },
40 | "LoadDefaultStylesheets": {
41 | "Panels": "Styles/panel.json",
42 | "MessageBoxBackdrop": "Styles/message_box_backdrop.json",
43 | "Paragraphs": "Styles/paragraph.json",
44 | "Titles": "Styles/title.json",
45 | "Labels": "Styles/label.json",
46 | "Buttons": "Styles/button.json",
47 | "HorizontalLines": "Styles/horizontal_line.json",
48 | "VerticalLines": "Styles/vertical_line.json",
49 | "CheckBoxes": "Styles/checkbox.json",
50 | "RadioButtons": "Styles/radio_button.json",
51 | "HorizontalSliders": "Styles/slider_horizontal.json",
52 | "VerticalSliders": "Styles/slider_vertical.json",
53 | "HorizontalSlidersHandle": "Styles/slider_handle.json",
54 | "VerticalSlidersHandle": "Styles/slider_handle.json",
55 | "ListPanels": "Styles/list_panel.json",
56 | "ListItems": "Styles/list_item.json",
57 | "DropDownPanels": "Styles/list_panel.json",
58 | "DropDownItems": "Styles/list_item.json",
59 | "DropDownIcon": "Styles/dropdown_icon.json",
60 | "VerticalScrollbars": "Styles/scrollbar_vertical.json",
61 | "VerticalScrollbarsHandle": "Styles/scrollbar_vertical_handle.json",
62 | "TextInput": "Styles/text_input.json",
63 | "NumericTextInput": "Styles/numeric_input.json",
64 | "NumericTextInputButton": "Styles/numeric_input_button.json",
65 | "HorizontalProgressBars": "Styles/progress_bar_horizontal.json",
66 | "HorizontalProgressBarsFill": "Styles/progress_bar_horizontal_fill.json",
67 | "HorizontalColorSliders": "Styles/color_slider_horizontal.json",
68 | "HorizontalColorSlidersHandle": "Styles/color_slider_horizontal_handle.json",
69 | "ColorPickers": "Styles/color_picker.json",
70 | "ColorPickersHandle": "Styles/color_picker_handle.json"
71 | }
72 | }
--------------------------------------------------------------------------------
/Iguina.Demo/Iguina.Demo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Iguina.Demo/readme.md:
--------------------------------------------------------------------------------
1 | # Iguina.Demo
2 |
3 | Implementation-agnostic demo project.
4 | This project loads the stylesheets and creates all the demo project entities.
5 | It requires drivers from the host application for rendering and input.
6 |
7 | ## Assets
8 |
9 | In assets you can find built-in UI themes that are used by the demo application.
10 |
11 | For full documentation, check out the readme file at the repository root.
--------------------------------------------------------------------------------
/Iguina.Tests/Iguina.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 | true
10 |
11 |
12 |
13 |
14 | all
15 | runtime; build; native; contentfiles; analyzers; buildtransitive
16 |
17 |
18 |
19 |
20 | all
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Iguina.Tests/NumericInputTests.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Entities;
2 |
3 | namespace Iguina.Tests
4 | {
5 | ///
6 | /// Tests for .
7 | ///
8 | public class NumericInputTests
9 | {
10 | [TestCase("5", 5, "5")]
11 | [TestCase("100", 100, "100")]
12 | [TestCase("0", 0, "0")]
13 | [TestCase("00", 0, "0")]
14 | [TestCase("000", 0, "0")]
15 | [TestCase("05", 5, "5")]
16 | [TestCase("005", 5, "5")]
17 | [TestCase("00500", 500, "500")]
18 | [TestCase("1.23", 1.23, "1.23")]
19 | [TestCase("0.23", 0.23, "0.23")]
20 | [TestCase("0.04", 0.04, "0.04")]
21 | [TestCase("0.004", 0.004, "0.004")]
22 | [TestCase("00.2", 0.2, "0.2")]
23 | [TestCase("000.2", 0.2, "0.2")]
24 | [TestCase("23.0", 23.0, "23.0")]
25 | [TestCase("23.00", 23.0, "23.00")]
26 | [TestCase("23.000", 23.0, "23.000")]
27 | [TestCase("02.3", 2.3, "2.3")]
28 | [TestCase("20.3", 20.3, "20.3")]
29 | [TestCase("0020.3", 20.3, "20.3")]
30 | [TestCase(".23", 0.23, "0.23")]
31 | [TestCase("2.", 2, "2.")]
32 | [TestCase("-5", -5, "-5")]
33 | [TestCase("-100", -100, "-100")]
34 | [TestCase("-0", -0, "-0")]
35 | [TestCase("-00", -0, "-0")]
36 | [TestCase("-000", -0, "-0")]
37 | [TestCase("-05", -5, "-5")]
38 | [TestCase("-005", -5, "-5")]
39 | [TestCase("-00500", -500, "-500")]
40 | [TestCase("-1.23", -1.23, "-1.23")]
41 | [TestCase("-0.23", -0.23, "-0.23")]
42 | [TestCase("-0.04", -0.04, "-0.04")]
43 | [TestCase("-0.004", -0.004, "-0.004")]
44 | [TestCase("-00.2", -0.2, "-0.2")]
45 | [TestCase("-000.2", -0.2, "-0.2")]
46 | [TestCase("-23.0", -23.0, "-23.0")]
47 | [TestCase("-23.00", -23.0, "-23.00")]
48 | [TestCase("-23.000", -23.0, "-23.000")]
49 | [TestCase("-0023.0", -23.0, "-23.0")]
50 | [TestCase("-.23", -0.23, "-0.23")]
51 | [TestCase("-2.", -2, "-2.")]
52 | [TestCase("-", 0, "-")]
53 | [TestCase("-01.00", -1.0, "-1.00")]
54 | [TestCase("-001.00", -1.0, "-1.00")]
55 | [TestCase("0.", 0, "0.")]
56 | [TestCase("0.0", 0, "0.0")]
57 | [TestCase("0.000", 0, "0.000")]
58 | [TestCase("00.0", 0, "0.0")]
59 | [TestCase("000.0", 0, "0.0")]
60 | [TestCase("000.000", 0, "0.000")]
61 | [TestCase(".", 0, "0.")]
62 | [TestCase(".0", 0, "0.0")]
63 | [TestCase(".000", 0, "0.000")]
64 | [TestCase("-0.", 0, "-0.")]
65 | [TestCase("-0.0", 0, "-0.0")]
66 | [TestCase("-0.000", 0, "-0.000")]
67 | [TestCase("-00.0", 0, "-0.0")]
68 | [TestCase("-000.0", 0, "-0.0")]
69 | [TestCase("-000.000", 0, "-0.000")]
70 | [TestCase("-.", 0, "-0.")]
71 | [TestCase("-.0", 0, "-0.0")]
72 | [TestCase("-.000", 0, "-0.000")]
73 | [TestCase("", 0, "")]
74 | [TestCase("x", 0, "")]
75 | public void TestEnterDecimalNumber(string input, decimal expectedValue, string? expectedText = null)
76 | {
77 | if (expectedText == null)
78 | expectedText = input;
79 |
80 | var system = new UISystem(new TestRenderer(), new TestInputProvider());
81 |
82 | var numericInput = new NumericInput(system);
83 |
84 | // Add each character on at a time, as if the user is typing it
85 |
86 | for (int i = 0; i < input.Length; i++)
87 | numericInput.Value += input[i];
88 |
89 | Assert.That(numericInput.NumericValue, Is.EqualTo(expectedValue), "Typed value mismatch");
90 | Assert.That(numericInput.Value, Is.EqualTo(expectedText), "Typed string mismatch");
91 |
92 | // Add the whole text at once
93 |
94 | numericInput = new NumericInput(system);
95 |
96 | numericInput.Value = input;
97 |
98 | Assert.That(numericInput.NumericValue, Is.EqualTo(expectedValue), "Set value mismatch");
99 | Assert.That(numericInput.Value, Is.EqualTo(expectedText), "Set string mismatch");
100 | }
101 |
102 | // todo: integer tests, i.e. not accepting decimal
103 | }
104 | }
--------------------------------------------------------------------------------
/Iguina.Tests/TestInputProvider.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Drivers;
3 |
4 | namespace Iguina.Tests
5 | {
6 | public class TestInputProvider : IInputProvider
7 | {
8 | public Point GetMousePosition()
9 | {
10 | return new Point(200, 100);
11 | }
12 |
13 | public bool IsMouseButtonDown(MouseButton btn)
14 | {
15 | return false;
16 | }
17 |
18 | public int GetMouseWheelChange()
19 | {
20 | return 0;
21 | }
22 |
23 | public int[] GetTextInput()
24 | {
25 | return [];
26 | }
27 |
28 | public TextInputCommands[] GetTextInputCommands()
29 | {
30 | return [];
31 | }
32 |
33 | public KeyboardInteractions? GetKeyboardInteraction()
34 | {
35 | return null;
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Iguina.Tests/TestRenderer.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Drivers;
3 |
4 | namespace Iguina.Tests
5 | {
6 | public class TestRenderer : IRenderer
7 | {
8 | private Rectangle? _scissorRegion;
9 |
10 |
11 | public Rectangle GetScreenBounds()
12 | {
13 | return new Rectangle(0, 0, 1280, 720);
14 | }
15 |
16 | public void DrawTexture(string? effectIdentifier, string textureId, Rectangle destRect, Rectangle sourceRect, Color color)
17 | {
18 | }
19 |
20 | public Point MeasureText(string text, string? fontId, int fontSize, float spacing)
21 | {
22 | return new Point(text.Length * 10, 20);
23 | }
24 |
25 | public int GetTextLineHeight(string? fontId, int fontSize)
26 | {
27 | return 20;
28 | }
29 |
30 | public void DrawText(string? effectIdentifier, string text, string? fontId, int fontSize, Point position, Color fillColor, Color outlineColor, int outlineWidth, float spacing)
31 | {
32 | }
33 |
34 | public void DrawRectangle(Rectangle rectangle, Color color)
35 | {
36 | }
37 |
38 | public void SetScissorRegion(Rectangle region)
39 | {
40 | _scissorRegion = region;
41 | }
42 |
43 | public Rectangle? GetScissorRegion()
44 | {
45 | return _scissorRegion;
46 | }
47 |
48 | public void ClearScissorRegion()
49 | {
50 | _scissorRegion = null;
51 | }
52 |
53 | public Color GetPixelFromTexture(string textureId, Point sourcePosition)
54 | {
55 | throw new NotImplementedException();
56 | }
57 |
58 | public Point? FindPixelOffsetInTexture(string textureId, Rectangle sourceRect, Color color, bool returnNearestColor)
59 | {
60 | throw new NotImplementedException();
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/Iguina.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.7.34031.279
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iguina.Demo.RayLib", "Iguina.Demo.RayLib\Iguina.Demo.RayLib.csproj", "{ECDC699B-77D7-41D4-B6E9-41B62028AFF9}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iguina", "Iguina\Iguina.csproj", "{32F4BCCC-EB5D-4C12-9237-2D11373B331B}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Iguina.Demo", "Iguina.Demo\Iguina.Demo.csproj", "{1374D1D9-8F36-4068-8612-F01D993EADDA}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iguina.Demo.MonoGame", "Iguina.Demo.MonoGame\Iguina.Demo.MonoGame.csproj", "{E85ECED4-439E-428F-B56F-32D78609CCB1}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iguina.Tests", "Iguina.Tests\Iguina.Tests.csproj", "{B9B47536-6B18-41CF-9213-F7EFA851483D}"
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 | {ECDC699B-77D7-41D4-B6E9-41B62028AFF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {ECDC699B-77D7-41D4-B6E9-41B62028AFF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {ECDC699B-77D7-41D4-B6E9-41B62028AFF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {ECDC699B-77D7-41D4-B6E9-41B62028AFF9}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {32F4BCCC-EB5D-4C12-9237-2D11373B331B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {32F4BCCC-EB5D-4C12-9237-2D11373B331B}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {32F4BCCC-EB5D-4C12-9237-2D11373B331B}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {32F4BCCC-EB5D-4C12-9237-2D11373B331B}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {1374D1D9-8F36-4068-8612-F01D993EADDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {1374D1D9-8F36-4068-8612-F01D993EADDA}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {1374D1D9-8F36-4068-8612-F01D993EADDA}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {1374D1D9-8F36-4068-8612-F01D993EADDA}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {E85ECED4-439E-428F-B56F-32D78609CCB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {E85ECED4-439E-428F-B56F-32D78609CCB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {E85ECED4-439E-428F-B56F-32D78609CCB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {E85ECED4-439E-428F-B56F-32D78609CCB1}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {B9B47536-6B18-41CF-9213-F7EFA851483D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {B9B47536-6B18-41CF-9213-F7EFA851483D}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {B9B47536-6B18-41CF-9213-F7EFA851483D}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {B9B47536-6B18-41CF-9213-F7EFA851483D}.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 = {B1C8F349-FC49-4EBB-BAD9-9CA93DB2B845}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/Iguina/Defs/Anchor.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// Positions in parent entity to position the entity from.
7 | /// The anchor also affect the UI entity's offset.
8 | ///
9 | public enum Anchor
10 | {
11 | ///
12 | /// Auto placement with one entity per row.
13 | /// Going left-to-right.
14 | ///
15 | AutoLTR,
16 |
17 | ///
18 | /// Auto placement in the same row, until exceeding parent width.
19 | /// Going left-to-right.
20 | ///
21 | AutoInlineLTR,
22 |
23 | ///
24 | /// Auto placement with one entity per row.
25 | /// Going right-to-left.
26 | ///
27 | AutoRTL,
28 |
29 | ///
30 | /// Auto placement in the same row, until exceeding parent width.
31 | /// Going right-to-left.
32 | ///
33 | AutoInlineRTL,
34 |
35 | ///
36 | /// Auto placement with one entity per row.
37 | /// Aligned to center.
38 | ///
39 | AutoCenter,
40 |
41 | ///
42 | /// Entity is aligned to parent top-left internal corner.
43 | ///
44 | TopLeft,
45 |
46 | ///
47 | /// Entity is aligned to parent top-center internal point.
48 | ///
49 | TopCenter,
50 |
51 | ///
52 | /// Entity is aligned to parent top-right internal corner.
53 | ///
54 | TopRight,
55 |
56 | ///
57 | /// Entity is aligned to parent bottom-left internal corner.
58 | ///
59 | BottomLeft,
60 |
61 | ///
62 | /// Entity is aligned to parent bottom-center internal point.
63 | ///
64 | BottomCenter,
65 |
66 | ///
67 | /// Entity is aligned to parent bottom-right internal corner.
68 | ///
69 | BottomRight,
70 |
71 | ///
72 | /// Entity is aligned to parent center-left internal point.
73 | ///
74 | CenterLeft,
75 |
76 | ///
77 | /// Entity is aligned to parent center internal point.
78 | ///
79 | Center,
80 |
81 | ///
82 | /// Entity is aligned to parent center-right internal point.
83 | ///
84 | CenterRight,
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Iguina/Defs/Color.cs:
--------------------------------------------------------------------------------
1 |
2 | using System.Globalization;
3 |
4 | namespace Iguina.Defs
5 | {
6 | ///
7 | /// Serializable color value.
8 | ///
9 | public struct Color
10 | {
11 | public byte R { get; set; }
12 | public byte G { get; set; }
13 | public byte B { get; set; }
14 | public byte A { get; set; }
15 |
16 | ///
17 | /// Parse color value from string.
18 | ///
19 | /// Color as string, in format RRGGBB or RRGGBBAA.
20 | /// Parsed color.
21 | public static Color Parse(string hex)
22 | {
23 | byte r, g, b, a = 255;
24 |
25 | if (hex.Length == 6)
26 | {
27 | r = byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
28 | g = byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
29 | b = byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
30 | }
31 | else if (hex.Length == 8)
32 | {
33 | r = byte.Parse(hex.Substring(0, 2), NumberStyles.HexNumber);
34 | g = byte.Parse(hex.Substring(2, 2), NumberStyles.HexNumber);
35 | b = byte.Parse(hex.Substring(4, 2), NumberStyles.HexNumber);
36 | a = byte.Parse(hex.Substring(6, 2), NumberStyles.HexNumber);
37 | }
38 | else
39 | {
40 | throw new ArgumentException("Invalid hex color format");
41 | }
42 |
43 | return new Color(r, g, b, a);
44 | }
45 |
46 | public Color()
47 | {
48 | }
49 |
50 | public Color(byte r, byte g, byte b, byte a)
51 | {
52 | R = r;
53 | G = g;
54 | B = b;
55 | A = a;
56 | }
57 |
58 | public static readonly Color White = new Color(255, 255, 255, 255);
59 | public static readonly Color Black = new Color(0, 0, 0, 255);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Iguina/Defs/CursorProperties.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// Describe how to render the cursor.
7 | ///
8 | public class CursorProperties
9 | {
10 | ///
11 | /// Texture identifier.
12 | ///
13 | public string TextureId { get; set; } = null!;
14 |
15 | ///
16 | /// The source rectangle of the cursor texture to draw.
17 | /// An empty rectangle (0, 0, 0, 0) will render the entire texture.
18 | ///
19 | public Rectangle SourceRect { get; set; }
20 |
21 | ///
22 | /// Fill color tint.
23 | ///
24 | public Color FillColor { get; set; } = new Color(255, 255, 255, 255);
25 |
26 | ///
27 | /// Effect to use while rendering the cursor.
28 | /// This can be used by the host application as actual shader name, a flag identifier to change rendering, or any other purpose.
29 | ///
30 | public string EffectIdentifier { get; set; } = null!;
31 |
32 | ///
33 | /// Cursor graphics offset from top-left corner, in pixels.
34 | ///
35 | public Point Offset { get; set; }
36 |
37 | ///
38 | /// Cursor scale.
39 | ///
40 | public float Scale { get; set; } = 1f;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Iguina/Defs/EntityState.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// Different states an entity can be in.
7 | ///
8 | public enum EntityState
9 | {
10 | ///
11 | /// Default inactive state.
12 | ///
13 | Default,
14 |
15 | ///
16 | /// Entity is currently being targeted. For example, the mouse points on the entity.
17 | ///
18 | Targeted,
19 |
20 |
21 |
22 | ///
23 | /// Entity is currently being interacted with, for example text input entity we're currently typing into, or a button that is being pressed.
24 | ///
25 | Interacted,
26 |
27 | ///
28 | /// Entity is checked. For checkbox, radio button, or other entities that have a 'checked' state.
29 | ///
30 | Checked,
31 |
32 | ///
33 | /// Entity is currently being targeted, and also checked. For example, the mouse points on the entity.
34 | ///
35 | TargetedChecked,
36 |
37 | ///
38 | /// Entity is currently focused, ie its the last entity the user interacted with, and will accept keyboard interactions.
39 | ///
40 | Focused,
41 |
42 | ///
43 | /// Entity is disabled.
44 | ///
45 | Disabled,
46 |
47 | ///
48 | /// Entity is disabled but also checked.
49 | ///
50 | DisabledChecked,
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Iguina/Defs/FramedTexture.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// Define a texture with internal source rect and frame source rect.
7 | /// When rendered on a region, it will tile-render the frame parts on the edges, and the center part repeating in the fill.
8 | ///
9 | public class FramedTexture
10 | {
11 | ///
12 | /// Texture identifier.
13 | ///
14 | public string TextureId { get; set; } = null!;
15 |
16 | ///
17 | /// The source rectangle of the center part, without the frame.
18 | ///
19 | public Rectangle InternalSourceRect
20 | {
21 | get
22 | {
23 | if (_internalSourceRect.Width == 0 && _internalSourceRect.Height == 0 && FrameWidth.HasValue)
24 | {
25 | _internalSourceRect = new Rectangle(
26 | ExternalSourceRect.X + FrameWidth.Value.X,
27 | ExternalSourceRect.Y + FrameWidth.Value.Y,
28 | ExternalSourceRect.Width - FrameWidth.Value.X * 2,
29 | ExternalSourceRect.Height - FrameWidth.Value.Y * 2);
30 | }
31 | return _internalSourceRect;
32 | }
33 | set => _internalSourceRect = value;
34 | }
35 |
36 | // internal source rect value
37 | Rectangle _internalSourceRect;
38 |
39 | ///
40 | /// Graphics frame width.
41 | /// If set, it will set the value of InternalSourceRect by calculating ExternalSourceRect - FrameWidth.
42 | ///
43 | public Point? FrameWidth
44 | {
45 | get => _frameWidth;
46 | set
47 | {
48 | _frameWidth = value;
49 | if (value.HasValue)
50 | {
51 | _internalSourceRect = new Rectangle();
52 | }
53 | }
54 | }
55 |
56 | // frame width value
57 | Point? _frameWidth;
58 |
59 | ///
60 | /// The source rectangle of the entire framed texture, including the frame.
61 | ///
62 | public Rectangle ExternalSourceRect { get; set; }
63 |
64 | ///
65 | /// Get the source rectangle of the top frame, without corners.
66 | ///
67 | public Rectangle TopSourceRect => new Rectangle(InternalSourceRect.Left, ExternalSourceRect.Top, InternalSourceRect.Width, InternalSourceRect.Top - ExternalSourceRect.Top);
68 |
69 | ///
70 | /// Get the source rectangle of the bottom frame, without corners.
71 | ///
72 | public Rectangle BottomSourceRect => new Rectangle(InternalSourceRect.Left, InternalSourceRect.Bottom, InternalSourceRect.Width, ExternalSourceRect.Bottom - InternalSourceRect.Bottom);
73 |
74 | ///
75 | /// Get the source rectangle of the left frame, without corners.
76 | ///
77 | public Rectangle LeftSourceRect => new Rectangle(ExternalSourceRect.Left, InternalSourceRect.Top, InternalSourceRect.Left - ExternalSourceRect.Left, InternalSourceRect.Height);
78 |
79 | ///
80 | /// Get the source rectangle of the right frame, without corners.
81 | ///
82 | public Rectangle RightSourceRect => new Rectangle(InternalSourceRect.Right, InternalSourceRect.Top, ExternalSourceRect.Right - InternalSourceRect.Right, InternalSourceRect.Height);
83 |
84 | ///
85 | /// Get the source rectangle of the top left corner.
86 | ///
87 | public Rectangle TopLeftSourceRect => new Rectangle(ExternalSourceRect.Left, ExternalSourceRect.Top, InternalSourceRect.Left - ExternalSourceRect.Left, InternalSourceRect.Top - ExternalSourceRect.Top);
88 |
89 | ///
90 | /// Get the source rectangle of the top right corner.
91 | ///
92 | public Rectangle TopRightSourceRect => new Rectangle(InternalSourceRect.Right, ExternalSourceRect.Top, ExternalSourceRect.Right - InternalSourceRect.Right, InternalSourceRect.Top - ExternalSourceRect.Top);
93 |
94 | ///
95 | /// Get the source rectangle of the bottom left corner.
96 | ///
97 | public Rectangle BottomLeftSourceRect => new Rectangle(ExternalSourceRect.Left, InternalSourceRect.Bottom, InternalSourceRect.Left - ExternalSourceRect.Left, ExternalSourceRect.Bottom - InternalSourceRect.Bottom);
98 |
99 | ///
100 | /// Get the source rectangle of the bottom right corner.
101 | ///
102 | public Rectangle BottomRightSourceRect => new Rectangle(InternalSourceRect.Right, InternalSourceRect.Bottom, ExternalSourceRect.Right - InternalSourceRect.Right, ExternalSourceRect.Bottom - InternalSourceRect.Bottom);
103 |
104 | ///
105 | /// Will scale frame and source parts by this factor.
106 | ///
107 | public float TextureScale { get; set; } = 1f;
108 |
109 | ///
110 | /// Offset in pixels.
111 | ///
112 | public Point Offset { get; set; }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Iguina/Defs/IconTexture.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Defs
3 | {
4 | ///
5 | /// A texture to render as icon, with size based on source rectangle.
6 | ///
7 | public class IconTexture
8 | {
9 | ///
10 | /// Texture identifier.
11 | ///
12 | public string TextureId { get; set; } = null!;
13 |
14 | ///
15 | /// The source rectangle of the texture to draw.
16 | ///
17 | public Rectangle SourceRect { get; set; }
18 |
19 | ///
20 | /// Will scale icon by this factor.
21 | ///
22 | public float TextureScale { get; set; } = 1f;
23 |
24 | ///
25 | /// If true, will center icon horizontally.
26 | ///
27 | public bool CenterHorizontally { get; set; }
28 |
29 | ///
30 | /// If true, will center icon vertically.
31 | ///
32 | public bool CenterVertically { get; set; }
33 |
34 | ///
35 | /// Icon offset in pixels.
36 | ///
37 | public Point Offset { get; set; }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Iguina/Defs/InputState.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | using Iguina.Drivers;
4 |
5 | namespace Iguina.Defs
6 | {
7 | ///
8 | /// All input properties for entities interactions.
9 | ///
10 | public struct InputState
11 | {
12 | ///
13 | /// Current frame state.
14 | ///
15 | internal CurrentInputState _Current;
16 |
17 | ///
18 | /// Previous frame state.
19 | ///
20 | internal CurrentInputState _Previous;
21 |
22 | ///
23 | /// Is mouse left button is currently down.
24 | ///
25 | public bool LeftMouseDown => _Current.LeftMouseButton;
26 |
27 | ///
28 | /// Is mouse right button is currently down.
29 | ///
30 | public bool RightMouseDown => _Current.RightMouseButton;
31 |
32 | ///
33 | /// Is mouse wheel button is currently down.
34 | ///
35 | public bool WheelMouseDown => _Current.WheelMouseButton;
36 |
37 | ///
38 | /// Was left mouse button pressed this frame.
39 | ///
40 | public bool LeftMousePressedNow => _Current.LeftMouseButton && !_Previous.LeftMouseButton;
41 |
42 | ///
43 | /// Was right mouse button pressed this frame.
44 | ///
45 | public bool RightMousePressedNow => _Current.RightMouseButton && !_Previous.RightMouseButton;
46 |
47 | ///
48 | /// Was mouse wheel button pressed this frame.
49 | ///
50 | public bool WheelMousePressedNow => _Current.WheelMouseButton && !_Previous.WheelMouseButton;
51 |
52 | ///
53 | /// Was left mouse button released this frame.
54 | ///
55 | public bool LeftMouseReleasedNow => !_Current.LeftMouseButton && _Previous.LeftMouseButton;
56 |
57 | ///
58 | /// Was right mouse button released this frame.
59 | ///
60 | public bool RightMouseReleasedNow => !_Current.RightMouseButton && _Previous.RightMouseButton;
61 |
62 | ///
63 | /// Was mouse wheel button released this frame.
64 | ///
65 | public bool WheelMouseReleasedNow => !_Current.WheelMouseButton && _Previous.WheelMouseButton;
66 |
67 | ///
68 | /// Mouse wheel change.
69 | ///
70 | public int MouseWheelChange => _Current.MouseWheelChange;
71 |
72 | ///
73 | /// Current mouse position.
74 | ///
75 | public Point MousePosition => _Current.MousePosition;
76 |
77 | ///
78 | /// Mouse movement this frame.
79 | ///
80 | public Point MouseMove => new Point(_Current.MousePosition.X - _Previous.MousePosition.X, _Current.MousePosition.Y - _Previous.MousePosition.Y);
81 |
82 | ///
83 | /// Get current frame text input characters.
84 | ///
85 | public int[] TextInput => _Current.TextInput;
86 |
87 | ///
88 | /// Get current frame text input commands.
89 | ///
90 | public TextInputCommands[] TextInputCommands => _Current.TextInputCommands;
91 |
92 | ///
93 | /// Get current keyboard interactions.
94 | ///
95 | public KeyboardInteractions? KeyboardInteraction => (_Current.KeyboardInteraction != _Previous.KeyboardInteraction) ? _Current.KeyboardInteraction : null;
96 |
97 | ///
98 | /// Get if keyboard select button is currently held down.
99 | ///
100 | public bool IsKeyboardSelectPressedDown => _Current.KeyboardInteraction == KeyboardInteractions.Select;
101 |
102 | ///
103 | /// Current screen bounds.
104 | ///
105 | public Rectangle ScreenBounds;
106 | }
107 |
108 | ///
109 | /// Input state for a specific frame.
110 | ///
111 | public struct CurrentInputState
112 | {
113 | public bool LeftMouseButton;
114 | public bool RightMouseButton;
115 | public bool WheelMouseButton;
116 | public Point MousePosition;
117 | public int MouseWheelChange;
118 | public int[] TextInput;
119 | public TextInputCommands[] TextInputCommands;
120 | public KeyboardInteractions? KeyboardInteraction;
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Iguina/Defs/MeasureUnits.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 |
4 | namespace Iguina.Defs
5 | {
6 | ///
7 | /// Define a UI measurement unit.
8 | /// For example, position, size, etc.
9 | ///
10 | public struct Measurement
11 | {
12 | ///
13 | /// Measure value.
14 | ///
15 | public float Value { get; set; }
16 |
17 | ///
18 | /// Measure unit type.
19 | ///
20 | [JsonConverter(typeof(JsonStringEnumConverter))]
21 | public MeasureUnit Units { get; set; }
22 |
23 | ///
24 | /// Set value as pixels.
25 | ///
26 | public void SetPixels(int value)
27 | {
28 | Value = (float)value;
29 | Units = MeasureUnit.Pixels;
30 | }
31 |
32 | ///
33 | /// Set value as percents.
34 | ///
35 | public void SetPercents(float percent)
36 | {
37 | Value = Math.Max(0f, percent);
38 | Units = MeasureUnit.PercentOfParent;
39 | }
40 |
41 | ///
42 | /// Get value in pixels.
43 | ///
44 | /// Relevant parent size.
45 | /// Measurement value in pixels.
46 | public int GetValueInPixels(int parentSize)
47 | {
48 | if (Units == MeasureUnit.Pixels) { return (int)Value; }
49 | return (int)MathF.Round(parentSize * (Value / 100f));
50 | }
51 | }
52 |
53 | ///
54 | /// GUI measure unit with X and Y components.
55 | ///
56 | public struct MeasureVector
57 | {
58 | ///
59 | /// Measure unit on X axis.
60 | ///
61 | public Measurement X;
62 |
63 | ///
64 | /// Measure unit on Y axis.
65 | ///
66 | public Measurement Y;
67 |
68 | ///
69 | /// Create and return measure vector with pixel values.
70 | ///
71 | public static MeasureVector FromPixels(int x, int y)
72 | {
73 | var ret = new MeasureVector();
74 | ret.SetPixels(x, y);
75 | return ret;
76 | }
77 |
78 | ///
79 | /// Create and return measure vector with float values.
80 | ///
81 | public static MeasureVector FromPercents(float x, float y)
82 | {
83 | var ret = new MeasureVector();
84 | ret.SetPercents(x, y);
85 | return ret;
86 | }
87 |
88 | ///
89 | /// Set value as pixels.
90 | ///
91 | public void SetPixels(int x, int y)
92 | {
93 | X.SetPixels(x);
94 | Y.SetPixels(y);
95 | }
96 |
97 | ///
98 | /// Set value as percent of parent size.
99 | ///
100 | public void SetPercents(float x, float y)
101 | {
102 | X.SetPercents(x);
103 | Y.SetPercents(y);
104 | }
105 | }
106 |
107 | ///
108 | /// Measure unit types.
109 | ///
110 | public enum MeasureUnit
111 | {
112 | ///
113 | /// Value is pixels.
114 | ///
115 | Pixels = 0,
116 |
117 | ///
118 | /// Value is percent of parent size.
119 | /// For example if the measure is talking about position x, it will be in perents of parent entity width.
120 | ///
121 | PercentOfParent = 1
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Iguina/Defs/Orientation.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// Defines entity orientation: vertical or horizontal.
7 | ///
8 | public enum Orientation
9 | {
10 | Horizontal,
11 | Vertical
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Iguina/Defs/OverflowMode.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Defs
3 | {
4 | ///
5 | /// Define how to handle child entities that go out of the entity's bounds.
6 | ///
7 | public enum OverflowMode
8 | {
9 | ///
10 | /// Child entities can overflow bounding rectangle freely.
11 | ///
12 | AllowOverflow,
13 |
14 | ///
15 | /// Child entities rendering will be cut off if they go out of bounding rectangle.
16 | ///
17 | HideOverflow,
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Iguina/Defs/Point.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Defs
3 | {
4 | ///
5 | /// Serializable point value.
6 | ///
7 | public struct Point
8 | {
9 | public int X { get; set; }
10 | public int Y { get; set; }
11 |
12 | public Point() { }
13 |
14 | public Point(int x, int y)
15 | {
16 | X = x;
17 | Y = y;
18 | }
19 |
20 | public static readonly Point Zero = new Point(0, 0);
21 |
22 | public static readonly Point One = new Point(1, 1);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Iguina/Defs/Rectangle.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Defs
3 | {
4 | ///
5 | /// Serializable rectangle.
6 | ///
7 | public struct Rectangle
8 | {
9 | public int X { get; set; }
10 | public int Y { get; set; }
11 | public int Width { get; set; }
12 | public int Height { get; set; }
13 |
14 | public int Left => X;
15 | public int Top => Y;
16 | public int Right => X + Width;
17 | public int Bottom => Y + Height;
18 | public Point Center => new Point(X + Width / 2, Y + Height / 2);
19 |
20 | public static readonly Rectangle Empty = new Rectangle();
21 |
22 | public Rectangle() { }
23 |
24 | public Rectangle(int x, int y, int width, int height)
25 | {
26 | X = x;
27 | Y = y;
28 | Width = width;
29 | Height = height;
30 | }
31 |
32 | ///
33 | /// Check if a point is container in rectangle.
34 | ///
35 | public bool Contains(Point point)
36 | {
37 | return point.X >= Left && point.X <= Right && point.Y >= Top && Y <= Bottom;
38 | }
39 |
40 | ///
41 | /// Merge two rectangles.
42 | ///
43 | public static Rectangle MergeRectangles(Rectangle rect1, Rectangle rect2)
44 | {
45 | int x1 = Math.Max(rect1.X, rect2.X);
46 | int y1 = Math.Max(rect1.Y, rect2.Y);
47 | int x2 = Math.Min(rect1.X + rect1.Width, rect2.X + rect2.Width);
48 | int y2 = Math.Min(rect1.Y + rect1.Height, rect2.Y + rect2.Height);
49 |
50 | int width = x2 - x1;
51 | int height = y2 - y1;
52 |
53 | if (width > 0 && height > 0)
54 | {
55 | return new Rectangle(x1, y1, width, height);
56 | }
57 | else
58 | {
59 | return new Rectangle(0, 0, 0, 0);
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Iguina/Defs/Sides.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// Define 4 sides values.
7 | ///
8 | public struct Sides
9 | {
10 | public int Left { get; set; }
11 | public int Right { get; set; }
12 | public int Top { get; set; }
13 | public int Bottom { get; set; }
14 |
15 | public Sides() { }
16 |
17 | public Sides(int left, int right, int top, int bottom)
18 | {
19 | Left = left;
20 | Right = right;
21 | Top = top;
22 | Bottom = bottom;
23 | }
24 |
25 | public void TurnToZero()
26 | {
27 | Left = Right = Top = Bottom = 0;
28 | }
29 |
30 | public void Set(int left, int right, int top, int bottom)
31 | {
32 | Left = left;
33 | Right = right;
34 | Top = top;
35 | Bottom = bottom;
36 | }
37 |
38 | public static readonly Sides Zero = new Sides(0, 0, 0, 0);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Iguina/Defs/StretchedTexture.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// A texture to render stretched over the given region.
7 | ///
8 | public class StretchedTexture
9 | {
10 | ///
11 | /// Texture identifier.
12 | ///
13 | public string TextureId { get; set; } = null!;
14 |
15 | ///
16 | /// The source rectangle of the texture to draw.
17 | ///
18 | public Rectangle SourceRect { get; set; }
19 |
20 | ///
21 | /// Add extra size to the sides of this texture when rendering it.
22 | ///
23 | public Sides? ExtraSize { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Iguina/Defs/SystemStyleSheet.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | namespace Iguina.Defs
4 | {
5 | ///
6 | /// System-level stylesheet for UI.
7 | /// This define general properties that are not related to any specific entity.
8 | ///
9 | public class SystemStyleSheet
10 | {
11 | ///
12 | /// UI Theme identifier.
13 | ///
14 | public string ThemeIdentifier { get; set; } = null!;
15 |
16 | ///
17 | /// Scale all fonts in the UI system by this factor.
18 | ///
19 | public float TextScale { get; set; } = 1f;
20 |
21 | ///
22 | /// Scale all textures in the UI system by this factor.
23 | ///
24 | public float TextureScale { get; set; } = 1f;
25 |
26 | ///
27 | /// Default texture to use when a texture is expected but not provided.
28 | ///
29 | public string DefaultTexture { get; set; } = null!;
30 |
31 | ///
32 | /// Scale all the cursor textures by this factor.
33 | ///
34 | public float CursorScale { get; set; } = 1f;
35 |
36 | ///
37 | /// Cursor to render in default state.
38 | ///
39 | public CursorProperties? CursorDefault { get; set; }
40 |
41 | ///
42 | /// Cursor to render while pointing on an interactable entity that is not locked or disabled.
43 | ///
44 | public CursorProperties? CursorInteractable { get; set; }
45 |
46 | ///
47 | /// Cursor to render while pointing on a disabled entity.
48 | ///
49 | public CursorProperties? CursorDisabled { get; set; }
50 |
51 | ///
52 | /// Cursor to render while pointing on a locked entity.
53 | ///
54 | public CursorProperties? CursorLocked { get; set; }
55 |
56 | ///
57 | /// How much space a row spacer unit takes, in pixels.
58 | /// This determine what the UI system defines as a default empty "row" size.
59 | ///
60 | public int RowSpaceHeight { get; set; } = 14;
61 |
62 | ///
63 | /// Lock entities to interactive state for at least this value in seconds, to make sure the 'interactive' state is properly displayed even for rapid clicks.
64 | ///
65 | public float TimeToLockInteractiveState { get; set; }
66 |
67 | ///
68 | /// Optional framed texture to render over focused entities.
69 | ///
70 | public FramedTexture? FocusedEntityOverlay { get; set; }
71 |
72 | ///
73 | /// General system icons. For example file and folder icons, used by files dialog.
74 | ///
75 | public Dictionary SystemIcons { get; set; } = new();
76 |
77 | ///
78 | /// Default stylesheets to load for entities.
79 | /// Key = name of stylesheet to load (for example 'Panels' for 'uiSystem.DefaultStylesheets.Panels'.
80 | /// Value = path, relative to the folder containing this stylesheet, to load from.
81 | ///
82 | public Dictionary LoadDefaultStylesheets { get; set; } = null!;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Iguina/Defs/TextAlignment.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Defs
3 | {
4 | ///
5 | /// Text alignment properties.
6 | ///
7 | public enum TextAlignment
8 | {
9 | ///
10 | /// Left to right alignment.
11 | ///
12 | Left,
13 |
14 | ///
15 | /// Right to left alignment.
16 | ///
17 | Right,
18 |
19 | ///
20 | /// Center alignment.
21 | ///
22 | Center
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Iguina/Drivers/IFilesProvider.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Drivers
3 | {
4 | ///
5 | /// An interface to provide file reading access.
6 | /// By default it uses the simple built-in C# text file reader, but you can override this to get files content from other sources.
7 | ///
8 | public interface IFilesProvider
9 | {
10 | ///
11 | /// Read entire text file from a given path.
12 | ///
13 | /// File path.
14 | /// File content as string.
15 | public string ReadAllText(string path);
16 | }
17 |
18 | ///
19 | /// Built in files provider that simply read text files.
20 | ///
21 | public class DefaultFilesProvider : IFilesProvider
22 | {
23 | public string ReadAllText(string path)
24 | {
25 | return File.ReadAllText(path);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Iguina/Drivers/IInputProvider.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Drivers
5 | {
6 | ///
7 | /// An interface to provide input functionality for the GUI system.
8 | /// This is one of the drivers your application needs to provide.
9 | ///
10 | public interface IInputProvider
11 | {
12 | ///
13 | /// Get current mouse position.
14 | ///
15 | ///
16 | /// This doesn't have to actually be a mouse. For example, you can get input from gamepad and emulate mouse for gamepad controls.
17 | ///
18 | /// Mouse position.
19 | Point GetMousePosition();
20 |
21 | ///
22 | /// Get the state of a mouse button.
23 | ///
24 | /// Mouse button to check.
25 | ///
26 | /// This doesn't have to actually be a mouse. For example, you can get input from gamepad and emulate mouse for gamepad controls.
27 | ///
28 | /// True if mouse button is currently down.
29 | bool IsMouseButtonDown(MouseButton btn);
30 |
31 | ///
32 | /// Get current mouse wheel value.
33 | /// -1 = mouse wheel scrolled up.
34 | /// 1 = mouse wheel scrolled down.
35 | /// 0 = no change to mouse wheel.
36 | ///
37 | /// Mouse wheel change value.
38 | int GetMouseWheelChange();
39 |
40 | ///
41 | /// Get characters input from typing.
42 | ///
43 | /// Its the input provider responsibility to add 'Repeat Delay' and 'Repeating Rate'. Iguina will not limit or impose any delays on typing speed.
44 | /// Characters that were pressed this frame, as characters unicode values.
45 | int[] GetTextInput();
46 |
47 | ///
48 | /// Get special text input commands from typing.
49 | ///
50 | /// Its the input provider responsibility to add 'Repeat Delay' and 'Repeat Rate'. Iguina will not limit or impose any delays on typing speed.
51 | /// Text commands that were pressed this frame.
52 | TextInputCommands[] GetTextInputCommands();
53 |
54 | ///
55 | /// Optionally get keyboard-based interactions.
56 | /// This input is used on currently focused entity.
57 | ///
58 | /// Keyboard interaction command, or null if there are no keyboard interactions.
59 | KeyboardInteractions? GetKeyboardInteraction();
60 | }
61 |
62 | ///
63 | /// Keyboard-based input commands.
64 | ///
65 | public enum KeyboardInteractions
66 | {
67 | ///
68 | /// Move up (typically on arrow up key press).
69 | ///
70 | MoveUp,
71 |
72 | ///
73 | /// Move down (typically on arrow down key press).
74 | ///
75 | MoveDown,
76 |
77 | ///
78 | /// Move left (typically on arrow left key press).
79 | ///
80 | MoveLeft,
81 |
82 | ///
83 | /// Move right (typically on arrow right key press).
84 | ///
85 | MoveRight,
86 |
87 | ///
88 | /// Select / click / toggle (typically on space / enter key press).
89 | ///
90 | Select
91 | }
92 |
93 | ///
94 | /// Special text input commands.
95 | ///
96 | public enum TextInputCommands
97 | {
98 | MoveCaretLeft,
99 | MoveCaretRight,
100 | MoveCaretUp,
101 | MoveCaretDown,
102 | Backspace,
103 | Delete,
104 | BreakLine,
105 | MoveCaretEnd,
106 | MoveCaretStart,
107 | MoveCaretEndOfLine,
108 | MoveCaretStartOfLine,
109 | }
110 |
111 | ///
112 | /// Mouse buttons.
113 | ///
114 | public enum MouseButton
115 | {
116 | Left,
117 | Wheel,
118 | Right
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/Iguina/Drivers/IRenderer.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Drivers
5 | {
6 | ///
7 | /// An interface to provide rendering functionality for the GUI system.
8 | /// This is one of the drivers your application needs to provide.
9 | ///
10 | public interface IRenderer
11 | {
12 | ///
13 | /// Get screen bounds, in pixels.
14 | ///
15 | /// Current screen bounds.
16 | Rectangle GetScreenBounds();
17 |
18 | ///
19 | /// Draw a texture.
20 | ///
21 | /// Effect to use, or null for default.
22 | /// Texture id to draw.
23 | /// Destination rectangle.
24 | /// Source rectangle.
25 | /// Rendering color.
26 | void DrawTexture(string? effectIdentifier, string textureId, Rectangle destRect, Rectangle sourceRect, Color color);
27 |
28 | ///
29 | /// Measure text size.
30 | ///
31 | /// Text to measure.
32 | /// Font identifier.
33 | /// Font size.
34 | /// Font spacing factor. 1f = default font spacing.
35 | /// Text size, in pixels.
36 | Point MeasureText(string text, string? fontId, int fontSize, float spacing);
37 |
38 | ///
39 | /// Get line height for a font id and size.
40 | ///
41 | /// Font identifier.
42 | /// Font size.
43 | /// Text line height.
44 | int GetTextLineHeight(string? fontId, int fontSize);
45 |
46 | ///
47 | /// Draw text.
48 | ///
49 | /// Effect to use, or null for default.
50 | /// Text to render.
51 | /// Font identifier to use, or null for default font.
52 | /// Font size.
53 | /// Text position.
54 | /// Text fill color.
55 | /// Text outline color.
56 | /// Outline width, in pixels.
57 | /// Font spacing factor. 1f = default font spacing.
58 | void DrawText(string? effectIdentifier, string text, string? fontId, int fontSize, Point position, Color fillColor, Color outlineColor, int outlineWidth, float spacing);
59 |
60 | ///
61 | /// Draw a filled rectangle.
62 | ///
63 | /// Rectangle region.
64 | /// Rectangle color.
65 | void DrawRectangle(Rectangle rectangle, Color color);
66 |
67 | ///
68 | /// Set a rectangle region we can render on.
69 | /// Any rendering that exceed this region, would be culled out.
70 | ///
71 | /// Visible region. Only pixels within this rectangle will be rendered.
72 | void SetScissorRegion(Rectangle region);
73 |
74 | ///
75 | /// Get current scissor region, if set.
76 | ///
77 | /// Current scissor region or null if not set.
78 | Rectangle? GetScissorRegion();
79 |
80 | ///
81 | /// Clear scissor region, if set.
82 | ///
83 | void ClearScissorRegion();
84 |
85 | ///
86 | /// Get pixel color from texture and offset.
87 | ///
88 | /// Texture id to get pixel color from.
89 | /// Source position in texture.
90 | /// Pixel color in source texture.
91 | Color GetPixelFromTexture(string textureId, Point sourcePosition);
92 |
93 | ///
94 | /// Locate an offset in a given texture and source region that has a specific color.
95 | ///
96 | /// Texture id to find pixel color from.
97 | /// Source region to search in.
98 | /// Color to find.
99 | /// If true and exact color is not found, will return nearest color offset instead.
100 | /// Pixel offset, from source rect top-left corner. If color is not found, will return null.
101 | Point? FindPixelOffsetInTexture(string textureId, Rectangle sourceRect, Color color, bool returnNearestColor);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Iguina/Entities/Button.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 | namespace Iguina.Entities
4 | {
5 | ///
6 | /// Button entity type.
7 | ///
8 | public class Button : CheckedEntity
9 | {
10 | ///
11 | /// Button label.
12 | ///
13 | public Paragraph Paragraph { get; private set; }
14 |
15 | ///
16 | internal override bool Interactable => true;
17 |
18 | ///
19 | /// Create the button.
20 | ///
21 | /// Parent UI system.
22 | /// Button stylesheet.
23 | /// Button text.
24 | public Button(UISystem system, StyleSheet? stylesheet, string text = "New Button") : base(system, stylesheet)
25 | {
26 | // create the button paragraph
27 | Paragraph = new Paragraph(system, stylesheet, text);
28 | Paragraph.DrawFillTexture = false;
29 | AddChildInternal(Paragraph);
30 | Paragraph.CopyStateFrom = this;
31 | OverflowMode = OverflowMode.HideOverflow;
32 | }
33 |
34 | ///
35 | /// Create the button with default stylesheets.
36 | ///
37 | /// Parent UI system.
38 | /// Button text.
39 | public Button(UISystem system, string text = "New Button") : this(system, system.DefaultStylesheets.Buttons, text)
40 | {
41 | }
42 |
43 | ///
44 | protected override MeasureVector GetDefaultEntityTypeSize()
45 | {
46 | var ret = new MeasureVector();
47 | ret.X.SetPercents(100f);
48 | ret.Y.SetPixels(54);
49 | return ret;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Iguina/Entities/Checkbox.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 | namespace Iguina.Entities
4 | {
5 | ///
6 | /// Checkbox entity type.
7 | ///
8 | public class Checkbox : CheckedEntity
9 | {
10 | ///
11 | /// Checkbox label.
12 | ///
13 | public Paragraph Paragraph { get; private set; }
14 |
15 | ///
16 | internal override bool Interactable => true;
17 |
18 | ///
19 | /// Create the checkbox.
20 | ///
21 | /// Parent UI system.
22 | /// Checkbox stylesheet.
23 | /// Checkbox text.
24 | public Checkbox(UISystem system, StyleSheet? stylesheet, string text = "New Checkbox") : base(system, stylesheet)
25 | {
26 | // create the checkbox paragraph
27 | Paragraph = new Paragraph(system, stylesheet, text);
28 | Paragraph.DrawFillTexture = false;
29 | AddChildInternal(Paragraph);
30 | Paragraph.CopyStateFrom = this;
31 |
32 | // make checkable
33 | ToggleCheckOnClick = true;
34 | CanClickToUncheck = true;
35 |
36 | }
37 |
38 | ///
39 | /// Create the checkbox with default stylesheets.
40 | ///
41 | /// Parent UI system.
42 | /// Checkbox text.
43 | public Checkbox(UISystem system, string text = "New Checkbox") : this(system, system.DefaultStylesheets.CheckBoxes, text)
44 | {
45 | }
46 |
47 | ///
48 | protected override MeasureVector GetDefaultEntityTypeSize()
49 | {
50 | var ret = new MeasureVector();
51 | ret.X.SetPercents(100f);
52 | ret.Y.SetPixels(54);
53 | return ret;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Iguina/Entities/CheckedEntity.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// Entity type that have 'Checked' property that can be checked / unchecked.
8 | ///
9 | public abstract class CheckedEntity : Entity
10 | {
11 | ///
12 | /// Set / get if this entity is in 'checked' state.
13 | /// Useful for buttons anc checkboxes, but can be set for any type of entity.
14 | ///
15 | public virtual bool Checked
16 | {
17 | get => _isChecked;
18 | set
19 | {
20 | // change checked state
21 | if (value != _isChecked)
22 | {
23 | // uncheck siblings
24 | if (ExclusiveSelection)
25 | {
26 | Parent?.IterateChildren((Entity entity) =>
27 | {
28 | var siblingAsCheckable = entity as CheckedEntity;
29 | if ((siblingAsCheckable != null) && (siblingAsCheckable != this) && (siblingAsCheckable.ExclusiveSelection))
30 | {
31 | siblingAsCheckable.Checked = false;
32 | }
33 | return true;
34 | });
35 | }
36 |
37 | // set value
38 | _isChecked = value;
39 |
40 | // invoke value change callbacks
41 | Events.OnValueChanged?.Invoke(this);
42 | UISystem.Events.OnValueChanged?.Invoke(this);
43 |
44 | // invoke checked / unchecked callbacks
45 | if (_isChecked)
46 | {
47 | Events.OnChecked?.Invoke(this);
48 | UISystem.Events.OnChecked?.Invoke(this);
49 | }
50 | else
51 | {
52 | Events.OnUnchecked?.Invoke(this);
53 | UISystem.Events.OnUnchecked?.Invoke(this);
54 | }
55 | }
56 | }
57 | }
58 |
59 | ///
60 | /// If true, this entity will check / uncheck itself when clicked on.
61 | ///
62 | public bool ToggleCheckOnClick = false;
63 |
64 | ///
65 | /// If true, when this entity is checked, all its direct siblings with this property will be automatically unchecked.
66 | /// This is used for things like radio button where only one option can be checked at any given moment.
67 | ///
68 | public bool ExclusiveSelection = false;
69 |
70 | ///
71 | /// If false, it will be impossible to uncheck this entity once its checked by clicking on it.
72 | /// However, if the 'ExclusiveSelection' is set, you can still uncheck it by checking another sibling.
73 | ///
74 | public bool CanClickToUncheck = true;
75 |
76 | ///
77 | public CheckedEntity(UISystem system, StyleSheet? stylesheet) : base(system, stylesheet)
78 | {
79 | }
80 |
81 | ///
82 | /// Toggle this entity checked state.
83 | ///
84 | public void ToggleCheckedState()
85 | {
86 | // disable unchecking
87 | if (!CanClickToUncheck && Checked)
88 | {
89 | return;
90 | }
91 |
92 | // toggle checked mode
93 | Checked = !Checked;
94 | }
95 |
96 | ///
97 | internal override void DoInteractions(InputState inputState)
98 | {
99 | // call base class to trigger events
100 | base.DoInteractions(inputState);
101 |
102 | // check / uncheck
103 | if (ToggleCheckOnClick)
104 | {
105 | if (inputState.LeftMouseReleasedNow)
106 | {
107 | ToggleCheckedState();
108 | }
109 | }
110 | }
111 |
112 | internal override void DoFocusedEntityInteractions(InputState inputState)
113 | {
114 | // call base class to trigger events
115 | base.DoFocusedEntityInteractions(inputState);
116 |
117 | // implement click via keyboard
118 | if (ToggleCheckOnClick)
119 | {
120 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.Select)
121 | {
122 | ToggleCheckedState();
123 | }
124 | }
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/Iguina/Entities/ColorPicker.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using System.Numerics;
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// Color picker lets the user pick a color from a 2d source texture, with X and Y axes.
8 | /// This is useful for color pickers that have a combination of hue and brightness.
9 | ///
10 | public class ColorPicker : Entity, IColorPicker
11 | {
12 | ///
13 | /// The entity used as the color picker handle.
14 | ///
15 | public Entity Handle { get; private set; }
16 |
17 | // last handle offset
18 | Point _lastHandleOffset = new Point(-1, -1);
19 |
20 | ///
21 | internal override bool Interactable => true;
22 |
23 | ///
24 | /// Get / set color picker value, as a color extracted from the source texture.
25 | ///
26 | public Color ColorValue
27 | {
28 | get => _colorValue;
29 | set
30 | {
31 | _colorValue = value;
32 | var srcTexture = SourceTextureData;
33 | var offset = UISystem.Renderer.FindPixelOffsetInTexture(srcTexture!.TextureId ?? UISystem.SystemStyleSheet.DefaultTexture, srcTexture.SourceRect, value, false);
34 | if (offset.HasValue)
35 | {
36 | SetHandleOffsetFromSource(offset.Value);
37 | }
38 | }
39 | }
40 |
41 | ///
42 | public void SetColorValueApproximate(Color value)
43 | {
44 | _colorValue = value;
45 | var srcTexture = SourceTextureData;
46 | var offset = UISystem.Renderer.FindPixelOffsetInTexture(srcTexture!.TextureId ?? UISystem.SystemStyleSheet.DefaultTexture, srcTexture.SourceRect, value, true);
47 | if (offset.HasValue)
48 | {
49 | SetHandleOffsetFromSource(offset.Value);
50 | }
51 | }
52 |
53 | // current color value
54 | Color _colorValue;
55 |
56 | // offset in source texture, from source rectangle top-left corner
57 | Point _offsetInSource;
58 |
59 | ///
60 | /// Get source stretch texture.
61 | ///
62 | StretchedTexture? SourceTextureData => StyleSheet.GetProperty("FillTextureStretched", State, null, OverrideStyles);
63 |
64 | ///
65 | /// Create the color picker.
66 | ///
67 | /// Parent UI system.
68 | /// Color picker stylesheet.
69 | /// Color picker handle stylesheet.
70 | public ColorPicker(UISystem system, StyleSheet? stylesheet, StyleSheet? handleStylesheet) : base(system, stylesheet)
71 | {
72 | // create handle
73 | Handle = new Entity(system, handleStylesheet);
74 | Handle.CopyStateFrom = this;
75 | Handle.Anchor = Anchor.TopLeft;
76 | Handle.TransferInteractionsTo = this;
77 | AddChildInternal(Handle);
78 | Handle.IgnoreScrollOffset = true;
79 | }
80 |
81 | ///
82 | /// Create the color picker with default stylesheets.
83 | ///
84 | /// Parent UI system.
85 | public ColorPicker(UISystem system) :
86 | this(system,
87 | system.DefaultStylesheets.ColorPickers,
88 | system.DefaultStylesheets.ColorPickersHandle)
89 | {
90 | }
91 |
92 | ///
93 | protected override void Update(float dt)
94 | {
95 | base.Update(dt);
96 |
97 | if ((_lastHandleOffset.X != _offsetInSource.X) || (_lastHandleOffset.Y != _offsetInSource.Y))
98 | {
99 | _lastHandleOffset = _offsetInSource;
100 | UpdateValueFromHandle();
101 | Events.OnValueChanged?.Invoke(this);
102 | }
103 | }
104 |
105 | ///
106 | /// Get factor from destination size to source size.
107 | ///
108 | Vector2 GetDestToSourceFactor()
109 | {
110 | var srcTexture = SourceTextureData;
111 | var srcWidth = (srcTexture?.SourceRect.Width ?? 1f);
112 | var srcHeight = (srcTexture?.SourceRect.Height ?? 1f);
113 | float factorX = srcWidth / (float)Math.Max(1, LastBoundingRect.Width);
114 | float factorY = srcHeight / (float)Math.Max(1, LastBoundingRect.Height);
115 | return new Vector2(factorX, factorY);
116 | }
117 |
118 | ///
119 | /// Set the color picker handle offset.
120 | ///
121 | /// Color picker offset, in pixels, from top-left corner.
122 | public void SetHandleOffset(Point offset)
123 | {
124 | var factor = GetDestToSourceFactor();
125 | _offsetInSource.X = (int)Math.Ceiling((float)offset.X * factor.X);
126 | _offsetInSource.Y = (int)Math.Ceiling((float)offset.Y * factor.Y);
127 | }
128 |
129 | ///
130 | /// Set the color picker handle offset, with offset representing pixel offset in texture from the picker source rectangle top left corner.
131 | ///
132 | /// Color picker offset, in pixels, from top-left corner of the texture source rectangle.
133 | public void SetHandleOffsetFromSource(Point offset)
134 | {
135 | _offsetInSource = offset;
136 | }
137 |
138 | ///
139 | /// Update color value from handle offset.
140 | ///
141 | void UpdateValueFromHandle()
142 | {
143 | var srcTexture = SourceTextureData;
144 | _colorValue = UISystem.Renderer.GetPixelFromTexture(srcTexture!.TextureId ?? UISystem.SystemStyleSheet.DefaultTexture ?? string.Empty,
145 | new Point(
146 | srcTexture.SourceRect.X + _offsetInSource.X,
147 | srcTexture.SourceRect.Y + _offsetInSource.Y)
148 | );
149 | }
150 |
151 | ///
152 | internal override void DoInteractions(InputState inputState)
153 | {
154 | // do base interactions
155 | base.DoInteractions(inputState);
156 |
157 | // select value via mouse
158 | if (inputState.LeftMouseDown)
159 | {
160 | SetHandleOffset(new Point(inputState.MousePosition.X - LastBoundingRect.X, inputState.MousePosition.Y - LastBoundingRect.Y));
161 | }
162 | }
163 |
164 | ///
165 | protected override DrawMethodResult Draw(DrawMethodResult parentDrawResult, DrawMethodResult? siblingDrawResult, bool dryRun)
166 | {
167 | // update handle offset
168 | var factor = GetDestToSourceFactor();
169 | Handle.Offset.X.Value = MathF.Ceiling(_offsetInSource.X / factor.X - Handle.LastBoundingRect.Width / 2);
170 | Handle.Offset.Y.Value = MathF.Ceiling(_offsetInSource.Y / factor.Y - Handle.LastBoundingRect.Height / 2);
171 | return base.Draw(parentDrawResult, siblingDrawResult, dryRun);
172 | }
173 |
174 | ///
175 | internal override void DoFocusedEntityInteractions(InputState inputState)
176 | {
177 | // call base class to trigger events
178 | base.DoFocusedEntityInteractions(inputState);
179 |
180 | // move value via keyboard - horizontal
181 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveLeft)
182 | {
183 | if (_offsetInSource.X > 0)
184 | {
185 | _offsetInSource.X--;
186 | UpdateValueFromHandle();
187 | }
188 | }
189 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveRight)
190 | {
191 | if (_offsetInSource.X < SourceTextureData?.SourceRect.Width)
192 | {
193 | _offsetInSource.X++;
194 | UpdateValueFromHandle();
195 | }
196 | }
197 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveUp)
198 | {
199 | if (_offsetInSource.Y > 0)
200 | {
201 | _offsetInSource.Y--;
202 | UpdateValueFromHandle();
203 | }
204 | }
205 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveDown)
206 | {
207 | if (_offsetInSource.Y < SourceTextureData?.SourceRect.Height)
208 | {
209 | _offsetInSource.Y++;
210 | UpdateValueFromHandle();
211 | }
212 | }
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/Iguina/Entities/ColorSlider.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 | namespace Iguina.Entities
4 | {
5 | ///
6 | /// Color slider is a color picker entity that is based on slider, where the user can pick a color from a range.
7 | /// This picker is useful for when the user can pick hue without choosing brightness or saturation.
8 | ///
9 | public class ColorSlider : Slider, IColorPicker
10 | {
11 | ///
12 | /// Get / set slider color value, based on the default state source texture.
13 | ///
14 | public Color ColorValue
15 | {
16 | get
17 | {
18 | var srcRect = SourceRectangle;
19 | if (Orientation == Orientation.Horizontal)
20 | {
21 | return UISystem.Renderer.GetPixelFromTexture(SourceTextureId, new Point(srcRect.Left + (int)Math.Round(ValuePercent * (srcRect.Width - 0.1f)), srcRect.Top + srcRect.Height / 2));
22 | }
23 | else
24 | {
25 | return UISystem.Renderer.GetPixelFromTexture(SourceTextureId, new Point(srcRect.Left + srcRect.Width / 2, srcRect.Top + (int)Math.Round(ValuePercent * (srcRect.Height - 0.1f))));
26 | }
27 | }
28 | set
29 | {
30 | var src = SourceRectangle;
31 | src.Y += src.Height / 2;
32 | src.Height = 1;
33 | var offset = UISystem.Renderer.FindPixelOffsetInTexture(SourceTextureId, SourceRectangle, value, false);
34 | if (offset.HasValue)
35 | {
36 | if (Orientation == Orientation.Horizontal)
37 | {
38 | ValueSafe = offset.Value.X;
39 | }
40 | else
41 | {
42 | ValueSafe = offset.Value.Y;
43 | }
44 | }
45 | }
46 | }
47 |
48 | ///
49 | /// Create the colors slider.
50 | ///
51 | /// Parent UI system.
52 | /// Slider stylesheet.
53 | /// Slider handle stylesheet.
54 | /// Slider orientation.
55 | public ColorSlider(UISystem system, StyleSheet? stylesheet, StyleSheet? handleStylesheet, Orientation orientation = Orientation.Horizontal) : base(system, stylesheet, handleStylesheet, orientation)
56 | {
57 | AutoSetRange = true;
58 | SetAutoRange();
59 | }
60 |
61 | ///
62 | /// Create the color slider with default stylesheets.
63 | ///
64 | /// Parent UI system.
65 | /// Slider orientation.
66 | public ColorSlider(UISystem system, Orientation orientation = Orientation.Horizontal) :
67 | this(system,
68 | (orientation == Orientation.Horizontal) ? (system.DefaultStylesheets.HorizontalColorSliders ?? system.DefaultStylesheets.HorizontalSliders) : (system.DefaultStylesheets.VerticalColorSliders ?? system.DefaultStylesheets.VerticalSliders),
69 | (orientation == Orientation.Horizontal) ? (system.DefaultStylesheets.HorizontalColorSlidersHandle ?? system.DefaultStylesheets.HorizontalSlidersHandle) : (system.DefaultStylesheets.VerticalColorSlidersHandle ?? system.DefaultStylesheets.VerticalSlidersHandle),
70 | orientation)
71 | {
72 | }
73 |
74 | ///
75 | public void SetColorValueApproximate(Color value)
76 | {
77 | var src = SourceRectangle;
78 | src.Y += src.Height / 2;
79 | src.Height = 1;
80 | var offset = UISystem.Renderer.FindPixelOffsetInTexture(SourceTextureId, SourceRectangle, value, true);
81 | if (offset.HasValue)
82 | {
83 | if (Orientation == Orientation.Horizontal)
84 | {
85 | ValueSafe = offset.Value.X;
86 | }
87 | else
88 | {
89 | ValueSafe = offset.Value.Y;
90 | }
91 | }
92 | }
93 |
94 | ///
95 | /// Get source stretch texture.
96 | ///
97 | StretchedTexture? SourceTextureData => StyleSheet.GetProperty("FillTextureStretched", State, null, OverrideStyles);
98 |
99 | ///
100 | /// Get slider source rectangle.
101 | ///
102 | Rectangle SourceRectangle => SourceTextureData?.SourceRect ?? Rectangle.Empty;
103 |
104 | ///
105 | /// Get slider source texture.
106 | ///
107 | string SourceTextureId => SourceTextureData?.TextureId ?? UISystem.SystemStyleSheet.DefaultTexture ?? string.Empty;
108 |
109 | ///
110 | protected override void SetAutoRange()
111 | {
112 | MinValue = 0;
113 | if (Orientation == Orientation.Horizontal)
114 | {
115 | MaxValue = Math.Max(SourceTextureData?.SourceRect.Width ?? LastBoundingRect.Width, 10);
116 | }
117 | else
118 | {
119 | MaxValue = Math.Max(SourceTextureData?.SourceRect.Height ?? LastBoundingRect.Height, 10);
120 | }
121 | StepsCount = (uint)MaxValue;
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/Iguina/Entities/DropDown.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// A list of items users can select items from, that collapses while not being interacted with.
8 | ///
9 | public class DropDown : ListBox
10 | {
11 | ///
12 | /// Is the dropdown currently opened?
13 | ///
14 | public bool IsOpened { get; private set; }
15 |
16 | ///
17 | protected override bool ShowListScrollbar => base.ShowListScrollbar && IsOpened;
18 |
19 | ///
20 | /// Text to show when no value is selected and dropdown is collapsed.
21 | ///
22 | public string? DefaultSelectedText = null;
23 |
24 | ///
25 | internal override bool TopMostInteractions => IsOpened;
26 |
27 | ///
28 | /// If defined, this will be the text to display when the dropdown is collapsed, regardless of the currently selected item or default text.
29 | ///
30 | public string? OverrideSelectedText = null;
31 |
32 | ///
33 | /// Styles to override stylesheet defaults, regardless of entity state, for the paragraph showing the selected value in closed state.
34 | ///
35 | public StyleSheetState OverrideClosedStateTextStyles
36 | {
37 | get => _selectedValueParagraph.OverrideStyles;
38 | set => _selectedValueParagraph.OverrideStyles = value;
39 | }
40 |
41 | // panel for selected value text
42 | Panel _selectedValuePanel;
43 | Paragraph _selectedValueParagraph;
44 |
45 | // icon entity, if stylesheet is defined for it
46 | Entity _icon = null!;
47 |
48 | ///
49 | /// Create the drop down.
50 | ///
51 | /// Parent UI system.
52 | /// Drop down panel stylesheet.
53 | /// Drop down box items stylesheet. If not set, will use the same as base stylesheet.
54 | /// Stylesheet for an arrow icon to add to the dropdown to reflect its state. If null, will not add this entity.
55 | public DropDown(UISystem system, StyleSheet? stylesheet, StyleSheet? itemsStylesheet = null, StyleSheet? arrowIconStylesheet = null) : base(system, stylesheet, itemsStylesheet)
56 | {
57 | // set as auto-height by default
58 | AutoHeight = true;
59 |
60 | // create panel with selected value paragraph
61 | _selectedValuePanel = new Panel(system, stylesheet);
62 | _selectedValueParagraph = _selectedValuePanel.AddChild(new Paragraph(system, itemsStylesheet ?? stylesheet, string.Empty, true));
63 | _selectedValuePanel.Size.X.SetPercents(100f);
64 | _selectedValuePanel.Size.Y.SetPixels(GetClosedStateHeight());
65 | _selectedValuePanel.IgnoreScrollOffset = true;
66 | _selectedValuePanel._overrideInteractableState = true;
67 | _selectedValuePanel.PassFocusTo = this;
68 | var padding = GetPadding();
69 | _selectedValuePanel.Offset.X.Value = -padding.Left;
70 | _selectedValuePanel.Offset.Y.Value = -padding.Top;
71 | _selectedValuePanel.OverrideStyles.ExtraSize = new Sides() { Right = padding.Left + padding.Right };
72 | _selectedValuePanel.ExtraMarginForInteractions = new Sides(padding.Left, padding.Right, padding.Top, 0);
73 | AddChildInternal(_selectedValuePanel);
74 |
75 | // create dropdown icon
76 | if (arrowIconStylesheet != null)
77 | {
78 | _icon = new Entity(UISystem, arrowIconStylesheet);
79 | _icon.CopyStateFrom = this;
80 | _icon.IgnoreInteractions = true;
81 | _icon.IgnoreScrollOffset = true;
82 | AddChildInternal(_icon);
83 | }
84 |
85 | // clicking on selected value panel will open / close dropdown
86 | _selectedValuePanel.Events.OnClick += (Entity entity) =>
87 | {
88 | ToggleList();
89 | };
90 | }
91 |
92 | ///
93 | /// Show / hide arrow icon.
94 | ///
95 | /// Should we show or hide the dropdown arrow icon.
96 | public void ShowArrowIcon(bool show)
97 | {
98 | if (_icon != null)
99 | {
100 | _icon.Visible = show;
101 | }
102 | }
103 |
104 | ///
105 | /// Create the drop down with default stylesheets.
106 | ///
107 | /// Parent UI system.
108 | public DropDown(UISystem system) : this(system,
109 | system.DefaultStylesheets.DropDownPanels ?? system.DefaultStylesheets.ListPanels ?? system.DefaultStylesheets.Panels,
110 | system.DefaultStylesheets.DropDownItems ?? system.DefaultStylesheets.ListItems ?? system.DefaultStylesheets.Paragraphs,
111 | system.DefaultStylesheets.DropDownIcon)
112 | {
113 | }
114 |
115 | ///
116 | protected override void SetAutoSizes(int maxWidth, int maxHeight)
117 | {
118 | // set auto width
119 | if (AutoWidth)
120 | {
121 | var width = maxWidth;
122 | if (AutoWidthMaxSize.HasValue && width > AutoWidthMaxSize.Value)
123 | {
124 | width = AutoWidthMaxSize.Value;
125 | }
126 | Size.X.SetPixels(width);
127 | }
128 |
129 | // set auto height
130 | if (AutoHeight)
131 | {
132 | var extraSize = _selectedValuePanel.GetExtraSize();
133 | var selectedPanelHeight = _selectedValuePanel.LastBoundingRect.Height + _selectedValuePanel.GetMarginAfter().Y + extraSize.Bottom + extraSize.Top;
134 | var height = (int)(ItemHeight * ((float)ItemsCount + 0.5f) + selectedPanelHeight);
135 | if (AutoHeightMaxSize.HasValue && height > AutoHeightMaxSize.Value)
136 | {
137 | height = AutoHeightMaxSize.Value;
138 | }
139 | Size.Y.SetPixels(height);
140 | }
141 | }
142 |
143 |
144 | ///
145 | /// Get the dropdown height, in pixels, when its closed.
146 | ///
147 | /// Drop down height in pixels when closed.
148 | public int GetClosedStateHeight(bool includePadding = true, bool includeExtraSize = true)
149 | {
150 | var padding = includePadding ? GetPadding() : Sides.Zero;
151 | var extra = includeExtraSize ? GetExtraSize() : Sides.Zero;
152 | return ItemHeight + padding.Top + padding.Bottom + extra.Top + extra.Bottom;
153 | }
154 |
155 | ///
156 | /// Set bounding rectangles size to its closed state.
157 | ///
158 | private void SetSizeToClosedState(ref Rectangle boundingRect, ref Rectangle internalBoundingRect)
159 | {
160 | internalBoundingRect.Height = GetClosedStateHeight();
161 | boundingRect.Height = internalBoundingRect.Height;
162 | }
163 |
164 | ///
165 | protected override int GetExtraParagraphsCount()
166 | {
167 | return -2; // -2 to compensate top panel that shows selected value
168 | }
169 |
170 | ///
171 | public override void SetVisibleItemsCount(int items)
172 | {
173 | AutoHeight = false;
174 | Size.Y.SetPixels(ItemHeight * (items + 2)); // +2 to compensate top panel that shows selected value
175 | }
176 |
177 | ///
178 | protected override void DrawEntityType(ref Rectangle boundingRect, ref Rectangle internalBoundingRect, DrawMethodResult parentDrawResult, DrawMethodResult? siblingDrawResult)
179 | {
180 | // special - if we are rendering in open mode, top most
181 | if (_isCurrentlyDrawingOpenedListTopMost)
182 | {
183 | // draw open list
184 | base.DrawEntityType(ref boundingRect, ref internalBoundingRect, parentDrawResult, siblingDrawResult);
185 |
186 | // this part makes sure the top panel border is not hidden
187 | Rectangle rect = boundingRect;
188 | rect.Height = GetClosedStateHeight();
189 | DrawFillTextures(rect);
190 | return;
191 | }
192 |
193 | // closed? resize to close state BEFORE rendering
194 | if (!IsOpened)
195 | {
196 | SetSizeToClosedState(ref boundingRect, ref internalBoundingRect);
197 | }
198 |
199 | // dropdown is opened - render as list top-most
200 | if (IsOpened)
201 | {
202 | // move the scrollbar under the selected value box
203 | {
204 | if (VerticalScrollbar != null)
205 | {
206 | var extra = GetExtraSize();
207 | var scrollbarOffset = (int)((GetClosedStateHeight() - extra.Bottom) * 0.85f);
208 | VerticalScrollbar.Offset.Y.Value = scrollbarOffset;
209 | VerticalScrollbar.OverrideStyles.ExtraSize = new Sides(0, 0, 0, -scrollbarOffset);
210 | }
211 | }
212 |
213 | // draw opened list in top-most mode at the end of frame
214 | DrawMethodResult parentResults = parentDrawResult;
215 | DrawMethodResult? siblingResults = siblingDrawResult;
216 | UISystem.RunAfterDrawingEntities(() =>
217 | {
218 | _isCurrentlyDrawingOpenedListTopMost = true;
219 | _DoDraw(parentResults, siblingResults, false);
220 | _isCurrentlyDrawingOpenedListTopMost = false;
221 | });
222 | }
223 | // dropdown is closed - render normally in close state
224 | else
225 | {
226 | base.DrawEntityType(ref boundingRect, ref internalBoundingRect, parentDrawResult, siblingDrawResult);
227 | }
228 |
229 | // opened? resize to close state AFTER rendering
230 | // this way we render the entire open list, but without pushing down auto anchors below (ie the entity only takes the size of its close state when positioning).
231 | if (IsOpened)
232 | {
233 | SetSizeToClosedState(ref boundingRect, ref internalBoundingRect);
234 | }
235 | }
236 | bool _isCurrentlyDrawingOpenedListTopMost = false;
237 |
238 | ///
239 | /// Close the dropdown list.
240 | ///
241 | public void CloseList()
242 | {
243 | IsOpened = false;
244 | }
245 |
246 | ///
247 | /// Open the dropdown list.
248 | ///
249 | public void OpenList()
250 | {
251 | IsOpened = true;
252 | }
253 |
254 | ///
255 | /// Toggle dropdown list state.
256 | ///
257 | public void ToggleList()
258 | {
259 | if (IsOpened)
260 | {
261 | CloseList();
262 | }
263 | else
264 | {
265 | OpenList();
266 | }
267 | }
268 |
269 | ///
270 | protected override void OnItemClicked(Entity entity)
271 | {
272 | // if closed, open the list
273 | if (!IsOpened)
274 | {
275 | OpenList();
276 | }
277 | // if opened, call default action and close the list
278 | else
279 | {
280 | base.OnItemClicked(entity);
281 | CloseList();
282 | }
283 | }
284 |
285 | ///
286 | protected override void SetParagraphs(int scrollOffset, int startIndex = 0)
287 | {
288 | if (IsOpened)
289 | {
290 | if (_paragraphs.Count > 0)
291 | {
292 | _paragraphs[0].Offset.Y.Value = ItemHeight / 2;
293 | }
294 | base.SetParagraphs(scrollOffset, startIndex);
295 | }
296 | else
297 | {
298 | foreach (var p in _paragraphs)
299 | {
300 | p.Visible = false;
301 | }
302 | }
303 | }
304 |
305 | ///
306 | internal override void PostUpdate(InputState inputState)
307 | {
308 | // update selected value text
309 | _selectedValueParagraph.Text = OverrideSelectedText ?? SelectedTextWithIcon ?? SelectedValue ?? DefaultSelectedText ?? string.Empty;
310 | _selectedValueParagraph.UseEmptyValueTextColor = (SelectedValue == null);
311 |
312 | // set icon state
313 | if (_icon != null)
314 | {
315 | _icon.LockedState = IsOpened ? EntityState.Interacted : null;
316 | }
317 |
318 | // if opened and click outside, close the list
319 | if (IsOpened && inputState.LeftMousePressedNow)
320 | {
321 | // clicked on closed state box? skip
322 | if (_selectedValuePanel.IsPointedOn(inputState.MousePosition))
323 | {
324 | return;
325 | }
326 |
327 | // if got here and point outside the list, close.
328 | if (!IsPointedOn(inputState.MousePosition))
329 | {
330 | CloseList();
331 | }
332 | }
333 | }
334 | }
335 | }
336 |
--------------------------------------------------------------------------------
/Iguina/Entities/HorizontalLine.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// A graphical horizontal line that separates between entities.
8 | ///
9 | public class HorizontalLine : Entity
10 | {
11 | ///
12 | /// Create the horizontal line.
13 | ///
14 | /// Parent UI system.
15 | /// Horizontal line stylesheet.
16 | public HorizontalLine(UISystem system, StyleSheet? stylesheet) : base(system, stylesheet)
17 | {
18 | IgnoreInteractions = true;
19 | }
20 |
21 | ///
22 | /// Create the horizontal line with default stylesheets.
23 | ///
24 | /// Parent UI system.
25 | public HorizontalLine(UISystem system) : this(system, system.DefaultStylesheets.HorizontalLines)
26 | {
27 | }
28 |
29 | ///
30 | protected override Anchor GetDefaultEntityTypeAnchor()
31 | {
32 | return Anchor.AutoCenter;
33 | }
34 |
35 | ///
36 | protected override MeasureVector GetDefaultEntityTypeSize()
37 | {
38 | var ret = new MeasureVector();
39 | ret.X.SetPercents(100f);
40 | ret.Y.SetPixels(8);
41 | return ret;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Iguina/Entities/IColorPicker.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 | namespace Iguina.Entities
4 | {
5 | ///
6 | /// Interface for an entity that is used to pick color.
7 | ///
8 | public interface IColorPicker
9 | {
10 | ///
11 | /// Get / set the picker color value.
12 | ///
13 | Color ColorValue { get; set; }
14 |
15 | ///
16 | /// Set the color value of this picker from a given color.
17 | /// If color is not found in source texture, it will set to the nearest found value.
18 | ///
19 | /// Color value to set to.
20 | void SetColorValueApproximate(Color value);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Iguina/Entities/Label.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// A label text entity.
8 | /// Same as a paragraph, but with different defaults values and stylesheet.
9 | ///
10 | public class Label : Paragraph
11 | {
12 | ///
13 | /// Create the label.
14 | ///
15 | /// Parent UI system.
16 | /// Label stylesheet.
17 | /// Label text.
18 | /// If true, this label will ignore user interactions.
19 | public Label(UISystem system, StyleSheet? stylesheet, string text = "New Label", bool ignoreInteractions = true) : base(system, stylesheet, text, ignoreInteractions)
20 | {
21 | }
22 |
23 | ///
24 | /// Create the label with default stylesheets.
25 | ///
26 | /// Parent UI system.
27 | /// Label text.
28 | /// If true, this label will ignore user interactions.
29 | public Label(UISystem system, string text = "New Label", bool ignoreInteractions = true) : this(system, system.DefaultStylesheets.Labels, text, ignoreInteractions)
30 | {
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Iguina/Entities/Panel.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Utils;
3 |
4 |
5 | namespace Iguina.Entities
6 | {
7 | ///
8 | /// A panel is a container for other entities.
9 | ///
10 | public class Panel : Entity
11 | {
12 | // so that scrollbars will work
13 | ///
14 | protected override bool WalkInternalChildren => true;
15 |
16 | ///
17 | /// Vertical scrollbar entity, if set.
18 | ///
19 | public Slider? VerticalScrollbar { get; private set; }
20 |
21 | ///
22 | protected override bool HaveScrollbars => VerticalScrollbar != null;
23 |
24 |
25 | ///
26 | /// If true, when the scrollbar value changes the offset of the child entities will smoothly interpolate to the new offset.
27 | /// If false, offset will be set immediately.
28 | ///
29 | public bool InterpolateScrollbarOffset = true;
30 |
31 | ///
32 | /// Interpolation speed, if scrollbar position is interpolated.
33 | ///
34 | public float ScrollbarInterpolationSpeed = 10f;
35 |
36 | // scrollbar interpolation value
37 | float _scrollbarOffset = 0f;
38 |
39 | ///
40 | /// Create the panel.
41 | ///
42 | /// Parent UI system.
43 | /// Panel stylesheet.
44 | public Panel(UISystem system, StyleSheet? stylesheet) : base(system, stylesheet)
45 | {
46 | }
47 |
48 | ///
49 | /// Create the panel with default stylesheets.
50 | ///
51 | /// Parent UI system.
52 | public Panel(UISystem system) : this(system, system.DefaultStylesheets.Panels)
53 | {
54 | }
55 |
56 | ///
57 | internal override void PerformMouseWheelScroll(int val)
58 | {
59 | if ((VerticalScrollbar != null) && (VerticalScrollbar.Visible))
60 | {
61 | VerticalScrollbar.PerformMouseWheelScroll(val);
62 | }
63 | else
64 | {
65 | base.PerformMouseWheelScroll(val);
66 | }
67 | }
68 |
69 | ///
70 | protected override void Update(float dt)
71 | {
72 | base.Update(dt);
73 |
74 | // update scrollbar
75 | if ((VerticalScrollbar != null) && IsCurrentlyVisible())
76 | {
77 | // scrollbar max value
78 | if (_autoSetScrollbarMax)
79 | {
80 | var maxScrollbarHeight = CalculateMaxScrollbarValue();
81 | if (maxScrollbarHeight > 1)
82 | {
83 | VerticalScrollbar.MaxValue = maxScrollbarHeight;
84 | VerticalScrollbar.KeyboardStep = VerticalScrollbar.MouseWheelStep = -Math.Clamp(maxScrollbarHeight / 10, 1, 100);
85 | VerticalScrollbar.Enabled = true;
86 | }
87 | }
88 |
89 | // current scroll value
90 | float scrollbarNewValue = -VerticalScrollbar.Value;
91 | _scrollbarOffset = InterpolateScrollbarOffset ? MathUtils.Lerp(_scrollbarOffset, scrollbarNewValue, dt * ScrollbarInterpolationSpeed) : scrollbarNewValue;
92 | }
93 | }
94 |
95 | ///
96 | /// Called after drawing a child.
97 | ///
98 | protected override void PostDrawingChild(DrawMethodResult? drawResult)
99 | {
100 | if (_autoSetScrollbarMax && (VerticalScrollbar != null) && (drawResult != null))
101 | {
102 | _maxHeightForScrollbar = Math.Max(_maxHeightForScrollbar, drawResult.Value.BoundingRect.Bottom - LastInternalBoundingRect.Top - LastInternalBoundingRect.Height);
103 | }
104 | }
105 | int _maxHeightForScrollbar = 1;
106 |
107 | ///
108 | /// Calculate scrollbar max value.
109 | ///
110 | protected virtual int CalculateMaxScrollbarValue()
111 | {
112 | return _maxHeightForScrollbar;
113 | }
114 |
115 | ///
116 | protected override MeasureVector GetDefaultEntityTypeSize()
117 | {
118 | var ret = new MeasureVector();
119 | ret.SetPixels(400, 400);
120 | return ret;
121 | }
122 |
123 | ///
124 | protected override Point GetScrollOffset()
125 | {
126 | if (VerticalScrollbar != null)
127 | {
128 | return new Point(0, (int)_scrollbarOffset);
129 | }
130 | return Point.Zero;
131 | }
132 |
133 | ///
134 | protected override Sides GetScrollExtraPadding()
135 | {
136 | if (VerticalScrollbar != null && VerticalScrollbar.Visible)
137 | {
138 | if (VerticalScrollbar.Anchor == Anchor.TopLeft || VerticalScrollbar.Anchor == Anchor.CenterLeft || VerticalScrollbar.Anchor == Anchor.BottomLeft)
139 | {
140 | return new Sides(VerticalScrollbar.LastBoundingRect.Width + VerticalScrollbar.GetMarginAfter().X, 0, 0, 0);
141 | }
142 | else if (VerticalScrollbar.Anchor == Anchor.TopRight || VerticalScrollbar.Anchor == Anchor.CenterRight || VerticalScrollbar.Anchor == Anchor.BottomRight)
143 | {
144 | return new Sides(0, VerticalScrollbar.LastBoundingRect.Width + VerticalScrollbar.GetMarginBefore().X, 0, 0);
145 | }
146 | }
147 | return Sides.Zero;
148 | }
149 |
150 | ///
151 | /// Create a vertical scrollbar for this panel with default stylesheet.
152 | ///
153 | /// If true, will set the scrollbar max value automatically based on panel height vs. most-bottom entity.
154 | public void CreateVerticalScrollbar(bool autoSetScrollbarMax = true)
155 | {
156 | CreateVerticalScrollbar(
157 | UISystem.DefaultStylesheets.VerticalScrollbars ?? UISystem.DefaultStylesheets.VerticalSliders,
158 | UISystem.DefaultStylesheets.VerticalScrollbarsHandle ?? UISystem.DefaultStylesheets.VerticalSlidersHandle);
159 | }
160 |
161 | ///
162 | /// Create a vertical scrollbar for this panel.
163 | ///
164 | /// Vertical scrollbar style.
165 | /// Vertical scrollbar handle style.
166 | /// If true, will set the scrollbar max value automatically based on panel height vs. most-bottom entity.
167 | public void CreateVerticalScrollbar(StyleSheet? stylesheet, StyleSheet? handleStylesheet, bool autoSetScrollbarMax = true)
168 | {
169 | VerticalScrollbar = new Slider(UISystem, stylesheet, handleStylesheet, Orientation.Vertical);
170 | VerticalScrollbar.Anchor = Anchor.TopRight;
171 | VerticalScrollbar.IncludeInInternalAutoAnchorCalculation = false;
172 | VerticalScrollbar.Value = 0;
173 | VerticalScrollbar.MaxValue = 0;
174 | VerticalScrollbar.FlippedDirection = true;
175 | VerticalScrollbar.KeyboardStep = VerticalScrollbar.MouseWheelStep = -1;
176 | VerticalScrollbar.IgnoreScrollOffset = true;
177 | _autoSetScrollbarMax = autoSetScrollbarMax;
178 | AddChildInternal(VerticalScrollbar, true);
179 | VerticalScrollbar.IgnoreScrollOffset = true;
180 | }
181 |
182 | // should we auto-set scrollbar max value?
183 | bool _autoSetScrollbarMax;
184 |
185 | ///
186 | /// Remove the vertical scrollbar for this panel, if set.
187 | ///
188 | public void RemoveVerticalScrollbar()
189 | {
190 | if (VerticalScrollbar != null)
191 | {
192 | RemoveChildInternal(VerticalScrollbar);
193 | VerticalScrollbar = null;
194 | }
195 | }
196 |
197 | ///
198 | internal override void DoInteractions(InputState inputState)
199 | {
200 | base.DoInteractions(inputState);
201 |
202 | // if got scrollbar, apply wheel to it
203 | if ((inputState.MouseWheelChange != 0) && (VerticalScrollbar != null))
204 | {
205 | VerticalScrollbar.PerformMouseWheelScroll(inputState.MouseWheelChange);
206 | }
207 | }
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/Iguina/Entities/ProgressBar.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Utils;
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// Progress bars are like sliders, but instead of moving a handle they 'fill' an internal entity.
8 | /// These entities are useful to show progress, health bars, etc.
9 | ///
10 | /// By default, progress bars have 'IgnoreInteractions' set to true. If you want the progress bar to behave like a slider and allow users to change its value, set it to false.
11 | public class ProgressBar : Slider
12 | {
13 | ///
14 | /// Create the progress bar.
15 | ///
16 | /// Parent UI system.
17 | /// Progress bar stylesheet.
18 | /// Progress bar fill stylesheet.
19 | /// Progress bar orientation.
20 | public ProgressBar(UISystem system, StyleSheet? stylesheet, StyleSheet? fillStylesheet, Orientation orientation = Orientation.Horizontal) : base(system, stylesheet, fillStylesheet, orientation)
21 | {
22 | Handle.IgnoreInteractions = IgnoreInteractions = true;
23 | Handle.Anchor = Handle.StyleSheet.DefaultAnchor ?? stylesheet?.DefaultAnchor ?? ((orientation == Orientation.Horizontal) ? Anchor.CenterLeft : Anchor.TopCenter);
24 | }
25 |
26 | ///
27 | /// Create the progress bar with default stylesheets.
28 | ///
29 | /// Parent UI system.
30 | /// Progress bar orientation.
31 | public ProgressBar(UISystem system, Orientation orientation = Orientation.Horizontal) :
32 | this(system,
33 | (orientation == Orientation.Horizontal) ? system.DefaultStylesheets.HorizontalProgressBars : system.DefaultStylesheets.VerticalProgressBars,
34 | (orientation == Orientation.Horizontal) ? system.DefaultStylesheets.HorizontalProgressBarsFill : system.DefaultStylesheets.VerticalProgressBarsFill,
35 | orientation)
36 | {
37 | }
38 |
39 | ///
40 | protected override void Update(float dt)
41 | {
42 | base.Update(dt);
43 | Handle.IgnoreInteractions = IgnoreInteractions;
44 | }
45 |
46 | ///
47 | protected override void UpdateHandle(float dt)
48 | {
49 | var valuePercent = ValuePercent;
50 | if (Orientation == Orientation.Horizontal)
51 | {
52 | if (InterpolateHandlePosition)
53 | {
54 | var currValue = Handle.Size.X.Value;
55 | Handle.Size.X.SetPercents(MathUtils.Lerp(currValue, valuePercent * 100f, dt * HandleInterpolationSpeed));
56 | }
57 | else
58 | {
59 | Handle.Size.X.SetPercents(valuePercent * 100f);
60 | }
61 | }
62 | else
63 | {
64 | if (InterpolateHandlePosition)
65 | {
66 | var currValue = Handle.Size.Y.Value;
67 | Handle.Size.Y.SetPercents(MathUtils.Lerp(currValue, valuePercent * 100f, dt * HandleInterpolationSpeed));
68 | }
69 | else
70 | {
71 | Handle.Size.Y.SetPercents(valuePercent * 100f);
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Iguina/Entities/RadioButton.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 | namespace Iguina.Entities
4 | {
5 | ///
6 | /// Radio button entity type.
7 | ///
8 | public class RadioButton : CheckedEntity
9 | {
10 | ///
11 | /// Radio button label.
12 | ///
13 | public Paragraph Paragraph { get; private set; }
14 |
15 | ///
16 | internal override bool Interactable => true;
17 |
18 |
19 | ///
20 | /// Create the radio button.
21 | ///
22 | /// Parent UI system.
23 | /// Radio button stylesheet.
24 | /// Radio button text.
25 | public RadioButton(UISystem system, StyleSheet? stylesheet, string text = "New Radio Button") : base(system, stylesheet)
26 | {
27 | // create the radio button paragraph
28 | Paragraph = new Paragraph(system, stylesheet, text);
29 | Paragraph.DrawFillTexture = false;
30 | AddChildInternal(Paragraph);
31 | Paragraph.CopyStateFrom = this;
32 |
33 | // make checkable
34 | ToggleCheckOnClick = true;
35 | CanClickToUncheck = false;
36 | ExclusiveSelection = true;
37 | }
38 |
39 | ///
40 | /// Create the radio button with default stylesheets.
41 | ///
42 | /// Parent UI system.
43 | /// Radio button text.
44 | public RadioButton(UISystem system, string text = "New Radio Button") : this(system,
45 | system.DefaultStylesheets.RadioButtons ?? system.DefaultStylesheets.CheckBoxes,
46 | text)
47 | {
48 | }
49 |
50 | ///
51 | protected override MeasureVector GetDefaultEntityTypeSize()
52 | {
53 | var ret = new MeasureVector();
54 | ret.X.SetPercents(100f);
55 | ret.Y.SetPixels(54);
56 | return ret;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Iguina/Entities/RowsSpacer.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 | namespace Iguina.Entities
4 | {
5 | ///
6 | /// An entity that doesn't do anything except for creating vertical space between entities that are set with auto anchor.
7 | ///
8 | public class RowsSpacer : Entity
9 | {
10 | ///
11 | /// Create the spacer.
12 | ///
13 | /// Parent UI system.
14 | /// How many empty 'rows' to to create. The height of each row is defined by the UI system stylesheet.
15 | public RowsSpacer(UISystem system, int rowsCount = 1) : base(system, null)
16 | {
17 | IgnoreInteractions = true;
18 | Size.Y.SetPixels(system.SystemStyleSheet.RowSpaceHeight * rowsCount);
19 | }
20 |
21 | ///
22 | protected override Anchor GetDefaultEntityTypeAnchor()
23 | {
24 | return Anchor.AutoCenter;
25 | }
26 |
27 | ///
28 | protected override MeasureVector GetDefaultEntityTypeSize()
29 | {
30 | var ret = new MeasureVector();
31 | ret.X.SetPercents(100f);
32 | ret.Y.SetPixels(8);
33 | return ret;
34 | }
35 |
36 | ///
37 | internal override void DebugDraw(bool debugDrawChildren)
38 | {
39 | if (!Visible) { return; }
40 |
41 | UISystem.Renderer.DrawRectangle(LastBoundingRect, new Color(255, 0, 0, 65));
42 |
43 | if (debugDrawChildren)
44 | {
45 | IterateChildren((Entity child) => {
46 | child.DebugDraw(debugDrawChildren);
47 | return true;
48 | });
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Iguina/Entities/Slider.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 | using Iguina.Utils;
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// A slider to get numeric input.
8 | ///
9 | public class Slider : Entity
10 | {
11 | ///
12 | /// The entity used as the slider handle.
13 | ///
14 | public Entity Handle { get; private set; }
15 |
16 | ///
17 | /// Slider orientation.
18 | ///
19 | public Orientation Orientation { get; private set; }
20 |
21 | ///
22 | /// How much to change value via mouse wheel scroll.
23 | ///
24 | public int MouseWheelStep = 1;
25 |
26 | ///
27 | /// How much to change value via keyboard interactions.
28 | ///
29 | public int KeyboardStep = 1;
30 |
31 | // current handle offset
32 | float _currHandleOffset;
33 |
34 | ///
35 | internal override bool LockFocusWhileMouseDown => true;
36 |
37 | ///
38 | internal override bool CanGetFocusWhileMouseIsDown => false;
39 |
40 | ///
41 | internal override bool Interactable => true;
42 |
43 | ///
44 | /// Slider min value.
45 | ///
46 | public virtual int MinValue
47 | {
48 | get => _minValue;
49 | set
50 | {
51 | if (_minValue != value)
52 | {
53 | _minValue = value;
54 | if (_maxValue < _minValue) { throw new ArgumentOutOfRangeException(nameof(value), "Slider min value must be smaller than max value!"); }
55 | if (_value < _minValue)
56 | {
57 | _value = _minValue;
58 | }
59 | }
60 | }
61 | }
62 | int _minValue = 0;
63 |
64 | ///
65 | /// Slider max value.
66 | ///
67 | public virtual int MaxValue
68 | {
69 | get => _maxValue;
70 | set
71 | {
72 | if (_maxValue != value)
73 | {
74 | _maxValue = value;
75 | if (_maxValue < _minValue) { throw new ArgumentOutOfRangeException(nameof(value), "Slider max value must be bigger than min value!"); }
76 | if (_value > _maxValue)
77 | {
78 | _value = _maxValue;
79 | }
80 | }
81 | }
82 | }
83 | int _maxValue = 10;
84 |
85 | ///
86 | /// Set / get current value.
87 | ///
88 | public int Value
89 | {
90 | get => _value;
91 | set
92 | {
93 | if (_value != value)
94 | {
95 | if (value < MinValue) { throw new ArgumentOutOfRangeException(nameof(value), "Slider value can't be smaller than min value!"); }
96 | if (value > MaxValue) { throw new ArgumentOutOfRangeException(nameof(value), "Slider value can't be bigger than max value!"); }
97 | _value = value;
98 | Events.OnValueChanged?.Invoke(this);
99 | UISystem.Events.OnValueChanged?.Invoke(this);
100 | }
101 | }
102 | }
103 | int _value = 5;
104 |
105 | ///
106 | /// Set current value, after clamping it to be between min and max.
107 | ///
108 | public int ValueSafe
109 | {
110 | get => Value;
111 | set
112 | {
113 | Value = Math.Clamp(value, MinValue, MaxValue);
114 | }
115 | }
116 |
117 | ///
118 | /// Slider steps count (or 0 for MaxValue - MinValue).
119 | ///
120 | public uint StepsCount
121 | {
122 | get => _stepsCount;
123 | set
124 | {
125 | if (_stepsCount != value)
126 | {
127 | _stepsCount = value;
128 | if (value > ValueRange)
129 | {
130 | throw new ArgumentOutOfRangeException(nameof(value), "Slider steps count can't be bigger than ValueRange (max - min) value!");
131 | }
132 | Value = MinValue;
133 | }
134 | }
135 | }
136 | uint _stepsCount = 0;
137 |
138 | ///
139 | /// Get value, as percent between 0.f and 1.f.
140 | ///
141 | public float ValuePercent => (float)(Value - MinValue) / (float)(MaxValue - MinValue);
142 |
143 | ///
144 | /// If true, slider direction will be flipped.
145 | ///
146 | public bool FlippedDirection = false;
147 |
148 | // so we can point and drag handle
149 | ///
150 | protected override bool WalkInternalChildren => true;
151 |
152 | ///
153 | /// Get the value range based on max and min values.
154 | ///
155 | public int ValueRange => MaxValue - MinValue;
156 |
157 | ///
158 | /// If true, this slider will set its range (min, max, and steps count) automatically, based on the entity size.
159 | ///
160 | public bool AutoSetRange = false;
161 |
162 | ///
163 | /// Create the slider.
164 | ///
165 | /// Parent UI system.
166 | /// Slider stylesheet.
167 | /// Slider handle stylesheet.
168 | /// Slider orientation.
169 | public Slider(UISystem system, StyleSheet? stylesheet, StyleSheet? handleStylesheet, Orientation orientation = Orientation.Horizontal) : base(system, stylesheet)
170 | {
171 | // set orientation and call default anchor and size again
172 | Orientation = orientation;
173 | CalculateDefaultAnchorAndSize();
174 |
175 | // create handle
176 | Handle = new Entity(system, handleStylesheet);
177 | Handle.CopyStateFrom = this;
178 | Handle.Anchor = (orientation == Orientation.Horizontal) ? Anchor.CenterLeft : Anchor.TopCenter;
179 | Handle.TransferInteractionsTo = this;
180 | AddChildInternal(Handle);
181 | Handle.IgnoreScrollOffset = true;
182 | }
183 |
184 | ///
185 | /// Create the slider with default stylesheets.
186 | ///
187 | /// Parent UI system.
188 | /// Slider orientation.
189 | public Slider(UISystem system, Orientation orientation = Orientation.Horizontal) :
190 | this(system,
191 | (orientation == Orientation.Horizontal) ? system.DefaultStylesheets.HorizontalSliders : system.DefaultStylesheets.VerticalSliders,
192 | (orientation == Orientation.Horizontal) ? system.DefaultStylesheets.HorizontalSlidersHandle : system.DefaultStylesheets.VerticalSlidersHandle,
193 | orientation)
194 | {
195 | }
196 |
197 | ///
198 | internal override void DoInteractions(InputState inputState)
199 | {
200 | // do base interactions
201 | base.DoInteractions(inputState);
202 |
203 | // special case - max value is 0
204 | if (ValueRange <= 0)
205 | {
206 | return;
207 | }
208 |
209 | // select value via mouse
210 | if (inputState.LeftMouseDown)
211 | {
212 | // get default steps count and step size
213 | var stepsCount = (int)StepsCount;
214 | var valueRange = ValueRange;
215 | if (stepsCount == 0) { stepsCount = valueRange; }
216 | var stepSize = valueRange / stepsCount;
217 |
218 | // get value in percent
219 | float valuePercent = (Orientation == Orientation.Horizontal) ?
220 | (((float)inputState.MousePosition.X - LastInternalBoundingRect.Left) / (float)LastInternalBoundingRect.Width) :
221 | (((float)inputState.MousePosition.Y - LastInternalBoundingRect.Top) / (float)LastInternalBoundingRect.Height);
222 | if (float.IsNaN(valuePercent)) { valuePercent = 0f; }
223 | if (valuePercent < 0f) { valuePercent = 0f; }
224 | if (valuePercent > 1f) { valuePercent = 1f; }
225 | if (((Orientation == Orientation.Horizontal) && FlippedDirection) ||
226 | ((Orientation == Orientation.Vertical) && !FlippedDirection))
227 | {
228 | valuePercent = 1f - valuePercent;
229 | }
230 | int relativeValue = (int)(MathF.Round((valuePercent * valueRange) / stepSize) * stepSize);
231 | Value = MinValue + (int)relativeValue;
232 | }
233 | }
234 |
235 | ///
236 | internal override void DoFocusedEntityInteractions(InputState inputState)
237 | {
238 | // call base class to trigger events
239 | base.DoFocusedEntityInteractions(inputState);
240 |
241 | // move value via keyboard - horizontal
242 | if (Orientation == Orientation.Horizontal)
243 | {
244 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveLeft)
245 | {
246 | ValueSafe -= KeyboardStep;
247 | }
248 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveRight)
249 | {
250 | ValueSafe += KeyboardStep;
251 | }
252 | }
253 | else
254 | {
255 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveUp)
256 | {
257 | ValueSafe += KeyboardStep;
258 | }
259 | if (inputState.KeyboardInteraction == Drivers.KeyboardInteractions.MoveDown)
260 | {
261 | ValueSafe -= KeyboardStep;
262 | }
263 | }
264 | }
265 |
266 | ///
267 | internal override void PerformMouseWheelScroll(int val)
268 | {
269 | if (MouseWheelStep != 0)
270 | {
271 | if (val > 0)
272 | {
273 | Value = Math.Clamp(Value + MouseWheelStep, MinValue, MaxValue);
274 | }
275 | if (val < 0)
276 | {
277 | Value = Math.Clamp(Value - MouseWheelStep, MinValue, MaxValue);
278 | }
279 | }
280 | }
281 |
282 | ///
283 | protected override void Update(float dt)
284 | {
285 | base.Update(dt);
286 | if (AutoSetRange)
287 | {
288 | SetAutoRange();
289 | }
290 | UpdateHandle(dt);
291 | }
292 |
293 | ///
294 | /// Auto set slider min, max, and steps count.
295 | ///
296 | protected virtual void SetAutoRange()
297 | {
298 | MinValue = 0;
299 | if (Orientation == Orientation.Horizontal)
300 | {
301 | MaxValue = Math.Max(LastBoundingRect.Width, 10);
302 | }
303 | else
304 | {
305 | MaxValue = Math.Max(LastBoundingRect.Height, 10);
306 | }
307 | StepsCount = (uint)MaxValue;
308 | }
309 |
310 | ///
311 | /// Update slider handle offset.
312 | ///
313 | protected virtual void UpdateHandle(float dt)
314 | {
315 | // not visible? skip
316 | if (!IsCurrentlyVisible()) { return; }
317 |
318 | // special case - no value range
319 | if (ValueRange <= 0)
320 | {
321 | Handle.Visible = false;
322 | return;
323 | }
324 | Handle.Visible = true;
325 |
326 | // get relative value
327 | int relativeValue = Value - MinValue;
328 |
329 | // set horizontal handle position
330 | if (Orientation == Orientation.Horizontal)
331 | {
332 | var offsetX = FlippedDirection ?
333 | (int)((1f - (float)relativeValue / ValueRange) * LastInternalBoundingRect.Width) :
334 | (int)(((float)relativeValue / ValueRange) * LastInternalBoundingRect.Width);
335 | float newOffset = offsetX - Handle.LastBoundingRect.Width / 2;
336 | _currHandleOffset = InterpolateHandlePosition ?
337 | MathUtils.Lerp(_currHandleOffset, newOffset, dt * HandleInterpolationSpeed) : newOffset;
338 | Handle.Offset.X.SetPixels((int)_currHandleOffset);
339 | }
340 | // set vertical handle position
341 | else
342 | {
343 | var offsetY = FlippedDirection ?
344 | (int)(((float)relativeValue / ValueRange) * LastInternalBoundingRect.Height) :
345 | (int)((1f - (float)relativeValue / ValueRange) * LastInternalBoundingRect.Height);
346 | float newOffset = offsetY - Handle.LastBoundingRect.Height / 2;
347 | _currHandleOffset = InterpolateHandlePosition ?
348 | MathUtils.Lerp(_currHandleOffset, newOffset, dt * HandleInterpolationSpeed) : newOffset;
349 | Handle.Offset.Y.SetPixels((int)_currHandleOffset);
350 | }
351 | }
352 |
353 | ///
354 | protected override MeasureVector GetDefaultEntityTypeSize()
355 | {
356 | var ret = new MeasureVector();
357 | if (Orientation == Orientation.Horizontal)
358 | {
359 | ret.X.SetPercents(100f);
360 | ret.Y.SetPixels(16);
361 | }
362 | else
363 | {
364 | ret.Y.SetPercents(100f);
365 | ret.X.SetPixels(16);
366 | }
367 | return ret;
368 | }
369 | }
370 | }
371 |
--------------------------------------------------------------------------------
/Iguina/Entities/Title.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// A title text entity.
8 | /// Same as a paragraph, but with different defaults values and stylesheet.
9 | ///
10 | public class Title : Paragraph
11 | {
12 | ///
13 | /// Create the title.
14 | ///
15 | /// Parent UI system.
16 | /// Title stylesheet.
17 | /// Title text.
18 | /// If true, this title will ignore user interactions.
19 | public Title(UISystem system, StyleSheet? stylesheet, string text = "New Title", bool ignoreInteractions = true) : base(system, stylesheet, text, ignoreInteractions)
20 | {
21 | }
22 |
23 | ///
24 | /// Create the title with default stylesheets.
25 | ///
26 | /// Parent UI system.
27 | /// Title text.
28 | /// If true, this title will ignore user interactions.
29 | public Title(UISystem system, string text = "New Title", bool ignoreInteractions = true) : this(system, system.DefaultStylesheets.Titles, text, ignoreInteractions)
30 | {
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Iguina/Entities/VerticalLine.cs:
--------------------------------------------------------------------------------
1 | using Iguina.Defs;
2 |
3 |
4 | namespace Iguina.Entities
5 | {
6 | ///
7 | /// A graphical vertical line that separates between entities.
8 | ///
9 | public class VerticalLine : Entity
10 | {
11 | ///
12 | /// Create the vertical line.
13 | ///
14 | /// Parent UI system.
15 | /// Vertical line stylesheet.
16 | public VerticalLine(UISystem system, StyleSheet? stylesheet) : base(system, stylesheet)
17 | {
18 | IgnoreInteractions = true;
19 | }
20 |
21 | ///
22 | /// Create the vertical line with default stylesheets.
23 | ///
24 | /// Parent UI system.
25 | public VerticalLine(UISystem system) : this(system, system.DefaultStylesheets.VerticalLines)
26 | {
27 | }
28 |
29 | ///
30 | protected override Anchor GetDefaultEntityTypeAnchor()
31 | {
32 | return Anchor.AutoInlineLTR;
33 | }
34 |
35 | ///
36 | protected override MeasureVector GetDefaultEntityTypeSize()
37 | {
38 | var ret = new MeasureVector();
39 | ret.X.SetPixels(8);
40 | ret.Y.SetPercents(100f);
41 | return ret;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Iguina/Iguina.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | 1.1.3
6 | enable
7 | enable
8 | True
9 | A framework-agnostic, cross platform, modern UI system for C#
10 | RonenNess
11 | Ronen Ness
12 |
13 | logo_no_text.png
14 | Iguina is a framework-agnostic, cross platform, modern UI system for C#.
15 |
16 | Its a spiritual successor to the GeonBit.UI library, but takes a more flexible and modern approach, and can be used with any framework, and not just MonoGame.
17 |
18 | readme.md
19 | https://github.com/RonenNess/Iguina
20 | UI;GUI;Game;User Interface;UX;MonoGame;RayLib
21 | LICENSE
22 |
23 |
24 |
25 |
26 | True
27 | \
28 |
29 |
30 | True
31 | \
32 |
33 |
34 | True
35 | \
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Iguina/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\Release\net8.0\publish\
10 | FileSystem
11 | <_TargetId>Folder
12 |
13 |
--------------------------------------------------------------------------------
/Iguina/Utils/MathUtils.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Iguina.Utils
3 | {
4 | ///
5 | /// Math utils.
6 | ///
7 | internal static class MathUtils
8 | {
9 | ///
10 | /// Lerp numeric value.
11 | ///
12 | public static float Lerp(float a, float b, float t)
13 | {
14 | return a + (b - a) * t;
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Iguina/readme.md:
--------------------------------------------------------------------------------
1 | # Iguina
2 |
3 | The main Iguina project.
4 | All the code is here.
5 |
6 | For full documentation, check out the readme file at the repository root.
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Ronen Ness
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ReadmeAssets/anchors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/anchors.png
--------------------------------------------------------------------------------
/ReadmeAssets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/demo.gif
--------------------------------------------------------------------------------
/ReadmeAssets/entity-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-button.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-checkbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-checkbox.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-colorpicker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-colorpicker.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-colorslider.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-colorslider.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-dropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-dropdown.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-hl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-hl.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-list.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-numericinput.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-numericinput.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-panel.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-paragraph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-paragraph.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-progressbars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-progressbars.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-sliders.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-sliders.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-textinput.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-textinput.png
--------------------------------------------------------------------------------
/ReadmeAssets/entity-vl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/entity-vl.png
--------------------------------------------------------------------------------
/ReadmeAssets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/logo.png
--------------------------------------------------------------------------------
/ReadmeAssets/logo_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/logo_bg.png
--------------------------------------------------------------------------------
/ReadmeAssets/logo_no_text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/logo_no_text.png
--------------------------------------------------------------------------------
/ReadmeAssets/mg_basic_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/mg_basic_example.png
--------------------------------------------------------------------------------
/ReadmeAssets/rl_basic_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/rl_basic_example.png
--------------------------------------------------------------------------------
/ReadmeAssets/save-file-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RonenNess/Iguina/4170370c9b949b8218c2fd026266a75d3cc899c3/ReadmeAssets/save-file-dialog.png
--------------------------------------------------------------------------------
/TODO.txt:
--------------------------------------------------------------------------------
1 | - GridView object.
2 | - TreeBox (like ListBox but with hierarchy).
3 | - CollapsiblePanel (panel that can open / close).
4 | - TabsView (multiple panels that you can switch between with tab buttons).
--------------------------------------------------------------------------------