├── GLGUI ├── bin │ ├── OpenTK.dll │ ├── OpenTK.GLControl.dll │ ├── OpenTK.dll.config │ └── OpenTK.GLControl.xml ├── GLGUI │ ├── GLSplitterOrientation.cs │ ├── GLFont │ │ ├── GLFontMonospacing.cs │ │ ├── GLFontAlignment.cs │ │ ├── GLFontTextNodeType.cs │ │ ├── GLFontTextPosition.cs │ │ ├── GLFontText.cs │ │ ├── GLFontTextNode.cs │ │ ├── GLFontCharacterKerningRule.cs │ │ ├── GLFontRenderHint.cs │ │ ├── GLFontGlyph.cs │ │ ├── GLFontTexture.cs │ │ ├── GLFontBuilderConfiguration.cs │ │ ├── GLFontVertexBuffer.cs │ │ ├── GLFontData.cs │ │ ├── GLFontKerningConfiguration.cs │ │ ├── GLFontRenderOptions.cs │ │ ├── GLFontKerningCalculator.cs │ │ ├── GLFontTextNodeList.cs │ │ └── GLFontBitmap.cs │ ├── GLSliderOrientation.cs │ ├── GLFlowDirection.cs │ ├── GLAnchorStyles.cs │ ├── Clipboard.cs │ ├── GLContextMenuEntry.cs │ ├── GLPadding.cs │ ├── GLContextMenu.cs │ ├── GLScrolledControl.cs │ ├── GLOptions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── GLGroupLayout.cs │ ├── GLViewport.cs │ ├── GLLabel.cs │ ├── GLLinkLabel.cs │ ├── GLDraw.cs │ ├── GLButton.cs │ ├── GLFlowLayout.cs │ ├── GLSplitLayout.cs │ ├── GLCursor.cs │ ├── GLCheckBox.cs │ ├── GLGUI.csproj │ ├── GLSlider.cs │ ├── GLScrollableControl.cs │ ├── GLFontTextNodeList.cs │ ├── GLForm.cs │ ├── GLSkin.cs │ └── Advanced │ │ └── GLDataControl.cs ├── GLGUI.GLControlExample │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── MainForm.cs │ ├── Program.cs │ ├── LineWriter.cs │ ├── GLGUI.GLControlExample.csproj │ └── GuiControl.cs ├── GLGUI.Example │ ├── Program.cs │ ├── LineWriter.cs │ ├── GLGUI.Example.csproj │ └── MainForm.cs └── GLGUI.sln ├── .gitignore └── README.md /GLGUI/bin/OpenTK.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ands/GLGUI/HEAD/GLGUI/bin/OpenTK.dll -------------------------------------------------------------------------------- /GLGUI/bin/OpenTK.GLControl.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ands/GLGUI/HEAD/GLGUI/bin/OpenTK.GLControl.dll -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .fuse_hidden* 3 | *.suo 4 | *.userprefs 5 | *.exe 6 | *.pdb 7 | *.mdb 8 | *.db 9 | *.manifest 10 | GLGUI.dll 11 | obj 12 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLSplitterOrientation.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLSplitterOrientation : byte 4 | { 5 | Horizontal, 6 | Vertical 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontMonospacing.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLFontMonospacing : byte 4 | { 5 | Natural = 0, 6 | Yes, 7 | No 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLSliderOrientation.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLSliderOrientation : byte 4 | { 5 | Horizontal, 6 | Vertical 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontAlignment.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLFontAlignment : byte 4 | { 5 | Left = 0, 6 | Right, 7 | Centre, 8 | Justify 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontTextNodeType.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | enum GLFontTextNodeType : byte 4 | { 5 | Word, 6 | LineBreak, 7 | Space, 8 | Tab 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFlowDirection.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLFlowDirection : byte 4 | { 5 | LeftToRight, 6 | TopDown, 7 | RightToLeft, 8 | BottomUp 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontTextPosition.cs: -------------------------------------------------------------------------------- 1 | using OpenTK; 2 | 3 | namespace GLGUI 4 | { 5 | public struct GLFontTextPosition 6 | { 7 | public int Index; 8 | public Vector2 Position; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLAnchorStyles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GLGUI 4 | { 5 | [Flags] 6 | public enum GLAnchorStyles : byte 7 | { 8 | None = 0x00, 9 | Left = 0x01, 10 | Top = 0x02, 11 | Right = 0x04, 12 | Bottom = 0x08, 13 | All = 0x0f 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/Clipboard.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GLGUI 4 | { 5 | #if !REFERENCE_WINDOWS_FORMS 6 | public static class Clipboard 7 | { 8 | private static string contents = ""; 9 | public static void SetText(string s) { contents = s; } 10 | public static bool ContainsText() { return contents.Length > 0; } 11 | public static string GetText() { return contents; } 12 | } 13 | #endif 14 | } -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLContextMenuEntry.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public class GLContextMenuEntry : GLButton 4 | { 5 | public GLContextMenuEntry(GLGui gui) : base(gui) 6 | { 7 | SkinEnabled = Gui.Skin.ContextMenuEntryEnabled; 8 | SkinPressed = Gui.Skin.ContextMenuEntryPressed; 9 | SkinHover = Gui.Skin.ContextMenuEntryHover; 10 | SkinDisabled = Gui.Skin.ContextMenuEntryDisabled; 11 | 12 | Click += (s, e) => Gui.CloseContextMenu(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | 3 | namespace GLGUI.GLControlExample 4 | { 5 | public partial class MainForm : Form 6 | { 7 | public MainForm() 8 | { 9 | Text = "GLGUI.GLControlExample"; 10 | Size = new System.Drawing.Size(1024, 600); 11 | 12 | var guiControl = new GuiControl(); 13 | guiControl.Dock = DockStyle.Fill; 14 | Controls.Add(guiControl); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontText.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace GLGUI 4 | { 5 | /// 6 | /// Class to hide GLFontTextNodeList and related classes from 7 | /// user whilst allowing a textNodeList to be passed around. 8 | /// 9 | public class GLFontText 10 | { 11 | internal GLFontTextNodeList textNodeList; 12 | internal SizeF maxSize; 13 | internal GLFontAlignment alignment; 14 | public GLFontVertexBuffer[] VertexBuffers; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLPadding.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public struct GLPadding 4 | { 5 | public int Left, Top, Right, Bottom; 6 | 7 | public int Horizontal { get { return Left + Right; } } 8 | public int Vertical { get { return Top + Bottom; } } 9 | 10 | public GLPadding(int all) 11 | { 12 | Left = Top = Right = Bottom = all; 13 | } 14 | public GLPadding(int left, int top, int right, int bottom) 15 | { 16 | Left = left; 17 | Top = top; 18 | Right = right; 19 | Bottom = bottom; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace GLGUI.GLControlExample 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new MainForm()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontTextNode.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | class GLFontTextNode 4 | { 5 | public GLFontTextNodeType Type; 6 | public string Text; 7 | public float Length; // pixel length (without tweaks) 8 | public float LengthTweak; // length tweak for justification 9 | 10 | public float ModifiedLength 11 | { 12 | get { return Length + LengthTweak; } 13 | } 14 | 15 | public GLFontTextNode(GLFontTextNodeType type, string text) 16 | { 17 | Type = type; 18 | Text = text; 19 | } 20 | 21 | public GLFontTextNode Next; 22 | public GLFontTextNode Previous; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontCharacterKerningRule.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLFontCharacterKerningRule : byte 4 | { 5 | /// 6 | /// Ordinary kerning 7 | /// 8 | Normal, 9 | /// 10 | /// All kerning pairs involving this character will kern by 0. This will 11 | /// override both Normal and NotMoreThanHalf for any pair. 12 | /// 13 | Zero, 14 | /// 15 | /// Any kerning pairs involving this character will not kern 16 | /// by more than half the minimum width of the two characters 17 | /// involved. This will override Normal for any pair. 18 | /// 19 | NotMoreThanHalf 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GLGUI 2 | ===== 3 | 4 | ![GLGUI Example](http://andsz.de/i/GLGUI.png) 5 | 6 | This OpenGL User Interface library is a WinForms-like object oriented GUI based on a custom OpenTK version which allows custom cursors (https://github.com/ands/opentk) and some QuickFont font generator code. 7 | Both GameWindows and GLControls, which use different input EventArgs, are supported. 8 | 9 | You can build the GLGUI assembly with or without System.Windows.Forms (=> with or without cursor support) by using the REFERENCE_WINDOWS_FORMS build flag. 10 | 11 | You can build the GLGUI assembly with or without OpenTK.GLControl (=> with or without OpenTK WinForms context support) by using the REFERENCE_OPENTK_GLCONTROL build flag. 12 | REFERENCE_OPENTK_GLCONTROL requires REFERENCE_WINDOWS_FORMS to be set. 13 | 14 | 15 | TODO 16 | ==== 17 | 18 | - Clean up (example) code 19 | - Add documentation 20 | - Get rid of last few legacy OpenGL dependencies 21 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontRenderHint.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public enum GLFontRenderHint : byte 4 | { 5 | /// 6 | /// Use AntiAliasGridFit when rendering the ttf character set to create the GLFont texture 7 | /// 8 | AntiAliasGridFit, 9 | /// 10 | /// Use AntiAlias when rendering the ttf character set to create the GLFont texture 11 | /// 12 | AntiAlias, 13 | /// 14 | /// Use ClearTypeGridFit if the font is smaller than 12, otherwise use AntiAlias 15 | /// 16 | SizeDependent, 17 | /// 18 | /// Use ClearTypeGridFit when rendering the ttf character set to create the GLFont texture 19 | /// 20 | ClearTypeGridFit, 21 | /// 22 | /// Use SystemDefault when rendering the ttf character set to create the GLFont texture 23 | /// 24 | SystemDefault 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLContextMenu.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLContextMenu : GLFlowLayout 8 | { 9 | public GLContextMenu(GLGui gui) : base(gui) 10 | { 11 | FlowDirection = GLFlowDirection.TopDown; 12 | AutoSize = true; 13 | 14 | Skin = Gui.Skin.ContextMenu; 15 | } 16 | 17 | protected override void UpdateLayout() 18 | { 19 | if (Controls.Count() > 0) 20 | { 21 | int maxWidth = 0; 22 | foreach (var entry in Controls) 23 | { 24 | entry.AutoSize = true; 25 | maxWidth = Math.Max(maxWidth, entry.Width); 26 | } 27 | foreach (var entry in Controls) 28 | { 29 | entry.AutoSize = false; 30 | entry.Size = new Size(maxWidth, entry.Height); 31 | } 32 | } 33 | 34 | base.UpdateLayout(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace GLGUI.Example 5 | { 6 | public static class Program 7 | { 8 | [STAThread] 9 | public static int Main(string[] args) 10 | { 11 | AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; 12 | 13 | new MainForm().Run(); 14 | return 0; 15 | } 16 | 17 | private static void HandleUnhandledException(Object o) 18 | { 19 | Exception e = o as Exception; 20 | 21 | if (e != null) 22 | { 23 | Console.WriteLine(e.ToString()); 24 | } 25 | } 26 | 27 | private static void OnUnhandledException(Object sender, UnhandledExceptionEventArgs e) 28 | { 29 | HandleUnhandledException(e.ExceptionObject); 30 | } 31 | 32 | private static void OnGuiUnhandedException(object sender, System.Threading.ThreadExceptionEventArgs e) 33 | { 34 | HandleUnhandledException(e.Exception); 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontGlyph.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace GLGUI 4 | { 5 | public class GLFontGlyph 6 | { 7 | /// 8 | /// Which texture page the glyph is on 9 | /// 10 | public int Page; 11 | 12 | /// 13 | /// The rectangle defining the glyphs position on the page 14 | /// 15 | public Rectangle Rect; 16 | 17 | /// 18 | /// How far the glyph would need to be vertically offset to be vertically in line with the tallest glyph in the set of all glyphs 19 | /// 20 | public int YOffset; 21 | 22 | /// 23 | /// Which character this glyph represents 24 | /// 25 | public char Character; 26 | 27 | public PointF TextureMin, TextureMax; 28 | 29 | public GLFontGlyph(int page, Rectangle rect, int yOffset, char character) 30 | { 31 | Page = page; 32 | Rect = rect; 33 | YOffset = yOffset; 34 | Character = character; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /GLGUI/bin/OpenTK.dll.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLScrolledControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | 5 | namespace GLGUI 6 | { 7 | internal class GLScrolledControl : GLControl 8 | { 9 | public Size TotalSize; 10 | 11 | public GLScrolledControl(GLGui gui) : base(gui) 12 | { 13 | AutoSize = true; 14 | HandleMouseEvents = false; 15 | } 16 | 17 | protected override void UpdateLayout() 18 | { 19 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 20 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 21 | Inner = new Rectangle(0, 0, outer.Width, outer.Height); 22 | 23 | TotalSize = new Size(0, 0); 24 | if (Controls.Any()) 25 | { 26 | TotalSize.Width = Controls.Max(c => c.Outer.Right); 27 | TotalSize.Height = Controls.Max(c => c.Outer.Bottom); 28 | } 29 | 30 | if (Parent != null && !Parent.AutoSize) // avoid repeated calls if autosize is true 31 | Parent.Invalidate(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18444 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GLGUI.GLControlExample.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.Example/LineWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace GLGUI.Example 6 | { 7 | public class LineWriter : TextWriter 8 | { 9 | public List Lines; 10 | public bool Changed = false; 11 | private StringBuilder currentLine; 12 | 13 | public LineWriter() 14 | { 15 | this.Lines = new List(1024); 16 | this.currentLine = new StringBuilder(256); 17 | } 18 | 19 | public override void Write(char value) 20 | { 21 | if (value == '\n') 22 | Flush(); 23 | else 24 | currentLine.Append(value); 25 | } 26 | 27 | public override void Flush() 28 | { 29 | Lines.Add(currentLine.ToString()); 30 | if (Lines.Count > 1024) 31 | Lines.RemoveAt(0); 32 | currentLine.Clear(); 33 | Changed = true; 34 | } 35 | 36 | public override Encoding Encoding 37 | { 38 | get { return System.Text.Encoding.UTF8; } 39 | } 40 | 41 | public void Clear() 42 | { 43 | Lines.Clear(); 44 | currentLine.Clear(); 45 | Changed = true; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/LineWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace GLGUI.GLControlExample 6 | { 7 | public class LineWriter : TextWriter 8 | { 9 | public List Lines; 10 | public bool Changed = false; 11 | private StringBuilder currentLine; 12 | 13 | public LineWriter() 14 | { 15 | this.Lines = new List(1024); 16 | this.currentLine = new StringBuilder(256); 17 | } 18 | 19 | public override void Write(char value) 20 | { 21 | if (value == '\n') 22 | Flush(); 23 | else 24 | currentLine.Append(value); 25 | } 26 | 27 | public override void Flush() 28 | { 29 | Lines.Add(currentLine.ToString()); 30 | if (Lines.Count > 1024) 31 | Lines.RemoveAt(0); 32 | currentLine.Clear(); 33 | Changed = true; 34 | } 35 | 36 | public override Encoding Encoding 37 | { 38 | get { return System.Text.Encoding.UTF8; } 39 | } 40 | 41 | public void Clear() 42 | { 43 | Lines.Clear(); 44 | currentLine.Clear(); 45 | Changed = true; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLOptions : GLFlowLayout 8 | { 9 | public GLCheckBox Selection { get { return selection; } set { if (Controls.Contains(value)) Select(value, EventArgs.Empty); } } 10 | 11 | public event EventHandler Changed; 12 | 13 | private GLCheckBox selection; 14 | 15 | public GLOptions(GLGui gui) : base(gui) 16 | { 17 | outer = new Rectangle(0, 0, 0, 0); 18 | } 19 | 20 | public override T Add(T control) 21 | { 22 | if (!(control is GLCheckBox)) 23 | throw new InvalidOperationException("only GLCheckBoxes are allowed on GLOptions instances"); 24 | base.Add(control); 25 | (control as GLCheckBox).Changed += Select; 26 | return control; 27 | } 28 | 29 | public override void Remove(GLControl control) 30 | { 31 | base.Remove(control); 32 | (control as GLCheckBox).Changed -= Select; 33 | } 34 | 35 | private void Select(object sender, EventArgs e) 36 | { 37 | if(selection != null) 38 | selection.Checked = false; 39 | 40 | GLCheckBox senderCheckBox = (GLCheckBox)sender; 41 | selection = senderCheckBox; 42 | senderCheckBox.Checked = true; 43 | 44 | if(Changed != null) 45 | Changed(sender, e); 46 | } 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GLGUI")] 9 | [assembly: AssemblyDescription("Fast WinForms-like OpenGL GUI library")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("ands")] 12 | [assembly: AssemblyProduct("GLGUI")] 13 | [assembly: AssemblyCopyright("Copyright © ands 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("8fdc3f4a-6ae4-440d-90a6-e5625e84be05")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyFileVersion("1.0.*")] 37 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GLGUI.GLControlExample")] 9 | [assembly: AssemblyDescription("Fast WinForms-like OpenGL GUI library GLControl example")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("ands")] 12 | [assembly: AssemblyProduct("GLGUI")] 13 | [assembly: AssemblyCopyright("Copyright © ands 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("bc14cb32-d0f9-44b3-ae64-40104de95020")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyFileVersion("1.0.*")] 37 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLGroupLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLGroupLayout : GLControl 8 | { 9 | public GLSkin.GLGroupLayoutSkin Skin { get { return skin; } set { skin = value; Invalidate(); } } 10 | 11 | private GLSkin.GLGroupLayoutSkin skin; 12 | 13 | public GLGroupLayout(GLGui gui) : base(gui) 14 | { 15 | Render += OnRender; 16 | 17 | skin = Gui.Skin.GroupLayout; 18 | 19 | outer = new Rectangle(0, 0, 100, 100); 20 | sizeMin = new Size(0, 0); 21 | sizeMax = new Size(int.MaxValue, int.MaxValue); 22 | } 23 | 24 | protected override void UpdateLayout() 25 | { 26 | if (AutoSize) 27 | { 28 | if (Controls.Count() > 0) 29 | { 30 | outer.Width = Controls.Max(c => c.Outer.Right) - Controls.Min(c => c.Outer.Left) + skin.Border.Horizontal; 31 | outer.Height = Controls.Max(c => c.Outer.Bottom) - Controls.Min(c => c.Outer.Top) + skin.Border.Vertical; 32 | } 33 | else 34 | { 35 | outer.Width = skin.Border.Horizontal; 36 | outer.Height = skin.Border.Vertical; 37 | } 38 | } 39 | 40 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 41 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 42 | Inner = new Rectangle(skin.Border.Left, skin.Border.Top, outer.Width - skin.Border.Horizontal, outer.Height - skin.Border.Vertical); 43 | } 44 | 45 | private void OnRender(object sender, double timeDelta) 46 | { 47 | GLDraw.Fill(ref skin.BorderColor); 48 | GLDraw.FillRect(Inner, ref skin.BackgroundColor); 49 | } 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontTexture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing.Imaging; 3 | using OpenTK.Graphics.OpenGL; 4 | 5 | namespace GLGUI 6 | { 7 | class GLFontTexture : IDisposable 8 | { 9 | public readonly int TextureID; 10 | public readonly int Width; 11 | public readonly int Height; 12 | 13 | public GLFontTexture(BitmapData dataSource) 14 | { 15 | Width = dataSource.Width; 16 | Height = dataSource.Height; 17 | 18 | GL.Enable(EnableCap.Texture2D); 19 | 20 | GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest); 21 | 22 | GL.GenTextures(1, out TextureID); 23 | GL.BindTexture(TextureTarget.Texture2D, TextureID); 24 | 25 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp); 26 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp); 27 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); 28 | GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); 29 | 30 | GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, Width, Height, 0, 31 | OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, dataSource.Scan0); 32 | 33 | GL.Disable(EnableCap.Texture2D); 34 | 35 | GLGui.usedTextures++; 36 | } 37 | 38 | public void Dispose() 39 | { 40 | GL.DeleteTexture(TextureID); 41 | GLGui.usedTextures--; 42 | } 43 | 44 | ~GLFontTexture() 45 | { 46 | lock(GLGui.toDispose) 47 | { 48 | GLGui.toDispose.Add(this); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontBuilderConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | /// 4 | /// What settings to use when building the font 5 | /// 6 | public class GLFontBuilderConfiguration 7 | { 8 | public GLFontKerningConfiguration KerningConfig = new GLFontKerningConfiguration(); 9 | 10 | /// 11 | /// Whether to use super sampling when building font texture pages 12 | /// 13 | /// 14 | /// 15 | public int SuperSampleLevels = 1; 16 | 17 | /// 18 | /// The standard width of texture pages (the page will 19 | /// automatically be cropped if there is extra space) 20 | /// 21 | public int PageWidth = 512; 22 | 23 | /// 24 | /// The standard height of texture pages (the page will 25 | /// automatically be cropped if there is extra space) 26 | /// 27 | public int PageHeight = 512; 28 | 29 | /// 30 | /// Whether to force texture pages to use a power of two. 31 | /// 32 | public bool ForcePowerOfTwo = true; 33 | 34 | /// 35 | /// The margin (on all sides) around glyphs when rendered to 36 | /// their texture page 37 | /// 38 | public int GlyphMargin = 2; 39 | 40 | /// 41 | /// Set of characters to support 42 | /// 43 | public string CharSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.:,;'\"(!?)+-*/=_{}[]@~#\\<>|^%$£&"; 44 | 45 | /// 46 | /// Which render hint to use when rendering the ttf character set to create the GLFont texture 47 | /// 48 | public GLFontRenderHint TextGenerationRenderHint = GLFontRenderHint.SizeDependent; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLViewport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Graphics.OpenGL; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLViewport : GLControl 8 | { 9 | public float AspectRatio { get { return (float)Inner.Width / (float)Inner.Height; } } 10 | public event RenderEventHandler RenderViewport; 11 | 12 | public GLViewport(GLGui gui) : base(gui) 13 | { 14 | Render += OnRender; 15 | 16 | outer = new Rectangle(0, 0, 256, 256); 17 | sizeMin = new Size(1, 1); 18 | sizeMax = new Size(int.MaxValue, int.MaxValue); 19 | } 20 | 21 | protected override void UpdateLayout() 22 | { 23 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 24 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 25 | Inner = new Rectangle(0, 0, outer.Width, outer.Height); 26 | } 27 | 28 | private void OnRender(object sender, double timeDelta) 29 | { 30 | if (RenderViewport == null) 31 | return; 32 | 33 | // save 34 | int[] mainViewport = new int[4]; 35 | GL.GetInteger(GetPName.Viewport, mainViewport); 36 | // TODO: get rid of these: 37 | GL.MatrixMode(MatrixMode.Projection); 38 | GL.PushMatrix(); 39 | GL.MatrixMode(MatrixMode.Modelview); 40 | GL.PushMatrix(); 41 | 42 | // render 43 | var vp = ToViewport(Inner); 44 | GL.Scissor(GLDraw.ScissorRect.X, Gui.Outer.Height - GLDraw.ScissorRect.Bottom, GLDraw.ScissorRect.Width, GLDraw.ScissorRect.Height); 45 | GL.Viewport(vp.X, mainViewport[3] - vp.Y - vp.Height, vp.Width, vp.Height); 46 | RenderViewport(this, timeDelta); 47 | 48 | // restore 49 | GL.Viewport(mainViewport[0], mainViewport[1], mainViewport[2], mainViewport[3]); 50 | // TODO: get rid of these: 51 | GL.MatrixMode(MatrixMode.Projection); 52 | GL.PopMatrix(); 53 | GL.MatrixMode(MatrixMode.Modelview); 54 | GL.PopMatrix(); 55 | } 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.Example/GLGUI.Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 10.0.0 7 | 2.0 8 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B} 9 | WinExe 10 | GLGUI.Example 11 | GLGUI.Example 12 | 13 | 14 | true 15 | full 16 | false 17 | ..\bin 18 | DEBUG; 19 | prompt 20 | 4 21 | x86 22 | false 23 | 24 | 25 | none 26 | true 27 | ..\bin 28 | prompt 29 | 4 30 | x86 31 | false 32 | 33 | 34 | 35 | 36 | False 37 | ..\bin\OpenTK.GLControl.dll 38 | 39 | 40 | 41 | 42 | 43 | ..\bin\OpenTK.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D} 55 | GLGUI 56 | 57 | 58 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLLabel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | #if REFERENCE_WINDOWS_FORMS 5 | using Clipboard = System.Windows.Forms.Clipboard; 6 | #endif 7 | 8 | namespace GLGUI 9 | { 10 | public class GLLabel : GLControl 11 | { 12 | public string Text { get { return text; } set { if (value != text) { text = value; Invalidate(); } } } 13 | public bool Enabled { get { return enabled; } set { enabled = value; Invalidate(); } } 14 | public GLSkin.GLLabelSkin SkinEnabled { get { return skinEnabled; } set { skinEnabled = value; Invalidate(); } } 15 | public GLSkin.GLLabelSkin SkinDisabled { get { return skinDisabled; } set { skinDisabled = value; Invalidate(); } } 16 | 17 | public bool WordWrap = false; 18 | public bool Multiline = false; 19 | 20 | private string text = ""; 21 | private GLFontText textProcessed = new GLFontText(); 22 | private SizeF textSize; 23 | private GLSkin.GLLabelSkin skinEnabled, skinDisabled; 24 | private GLSkin.GLLabelSkin skin; 25 | private bool enabled = true; 26 | 27 | public GLLabel(GLGui gui) : base(gui) 28 | { 29 | Render += OnRender; 30 | 31 | skinEnabled = Gui.Skin.LabelEnabled; 32 | skinDisabled = Gui.Skin.LabelDisabled; 33 | 34 | outer = new Rectangle(0, 0, 0, 0); 35 | sizeMin = new Size(1, (int)skinEnabled.Font.LineSpacing + skinEnabled.Padding.Vertical); 36 | sizeMax = new Size(int.MaxValue, int.MaxValue); 37 | 38 | HandleMouseEvents = false; 39 | } 40 | 41 | protected override void UpdateLayout() 42 | { 43 | skin = Enabled ? skinEnabled : skinDisabled; 44 | 45 | textSize = skin.Font.ProcessText(textProcessed, text, 46 | new SizeF(WordWrap ? (AutoSize ? SizeMax.Width - skin.Padding.Horizontal : outer.Width - skin.Padding.Horizontal) : float.MaxValue, Multiline ? (AutoSize ? float.MaxValue : outer.Height - skin.Padding.Vertical) : skin.Font.LineSpacing), 47 | skin.TextAlign); 48 | 49 | if (AutoSize) 50 | { 51 | outer.Width = (int)textSize.Width + skin.Padding.Horizontal; 52 | outer.Height = (int)textSize.Height + skin.Padding.Vertical; 53 | } 54 | 55 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 56 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 57 | 58 | Inner = new Rectangle(skin.Padding.Left, skin.Padding.Top, outer.Width - skin.Padding.Horizontal, outer.Height - skin.Padding.Vertical); 59 | } 60 | 61 | private void OnRender(object sender, double timeDelta) 62 | { 63 | GLDraw.Fill(ref skin.BackgroundColor); 64 | GLDraw.Text(textProcessed, Inner, ref skin.Color); 65 | } 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.18444 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GLGUI.GLControlExample.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GLGUI.GLControlExample.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLLinkLabel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Input; 4 | 5 | #if REFERENCE_WINDOWS_FORMS 6 | using Clipboard = System.Windows.Forms.Clipboard; 7 | #endif 8 | 9 | namespace GLGUI 10 | { 11 | public class GLLinkLabel : GLControl 12 | { 13 | public string Text { get { return text; } set { if (value != text) { text = value; Invalidate(); } } } 14 | public bool Enabled { get { return enabled; } set { enabled = value; Invalidate(); } } 15 | public GLSkin.GLLabelSkin SkinEnabled { get { return skinEnabled; } set { skinEnabled = value; Invalidate(); } } 16 | public GLSkin.GLLabelSkin SkinDisabled { get { return skinDisabled; } set { skinDisabled = value; Invalidate(); } } 17 | 18 | public bool WordWrap = false; 19 | public bool Multiline = false; 20 | 21 | public event EventHandler Click; 22 | 23 | private string text = ""; 24 | private GLFontText textProcessed = new GLFontText(); 25 | private SizeF textSize; 26 | private GLSkin.GLLabelSkin skinEnabled, skinDisabled; 27 | private GLSkin.GLLabelSkin skin; 28 | private bool enabled = true; 29 | 30 | public GLLinkLabel(GLGui gui) : base(gui) 31 | { 32 | Render += OnRender; 33 | MouseUp += OnMouseUp; 34 | MouseEnter += OnMouseEnter; 35 | MouseLeave += OnMouseLeave; 36 | 37 | skinEnabled = Gui.Skin.LinkLabelEnabled; 38 | skinDisabled = Gui.Skin.LinkLabelDisabled; 39 | 40 | outer = new Rectangle(0, 0, 0, 0); 41 | sizeMin = new Size(1, (int)skinEnabled.Font.LineSpacing + skinEnabled.Padding.Vertical); 42 | sizeMax = new Size(int.MaxValue, int.MaxValue); 43 | 44 | HandleMouseEvents = false; 45 | } 46 | 47 | protected override void UpdateLayout() 48 | { 49 | skin = Enabled ? skinEnabled : skinDisabled; 50 | 51 | textSize = skin.Font.ProcessText(textProcessed, text, 52 | new SizeF(WordWrap ? (AutoSize ? SizeMax.Width - skin.Padding.Horizontal : outer.Width - skin.Padding.Horizontal) : float.MaxValue, Multiline ? (AutoSize ? float.MaxValue : outer.Height - skin.Padding.Vertical) : skin.Font.LineSpacing), 53 | skin.TextAlign); 54 | 55 | if (AutoSize) 56 | { 57 | outer.Width = (int)textSize.Width + skin.Padding.Horizontal; 58 | outer.Height = (int)textSize.Height + skin.Padding.Vertical; 59 | } 60 | 61 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 62 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 63 | 64 | Inner = new Rectangle(skin.Padding.Left, skin.Padding.Top, outer.Width - skin.Padding.Horizontal, outer.Height - skin.Padding.Vertical); 65 | } 66 | 67 | private void OnRender(object sender, double timeDelta) 68 | { 69 | GLDraw.Fill(ref skin.BackgroundColor); 70 | GLDraw.Text(textProcessed, Inner, ref skin.Color); 71 | //GLDraw.Line(Inner.Left, Inner.Bottom, Inner.Right, Inner.Bottom, ref skin.Color); // very ugly on windows : / 72 | } 73 | 74 | private void OnMouseUp(object sender, MouseButtonEventArgs e) 75 | { 76 | if (enabled && e.Button == MouseButton.Left) 77 | { 78 | Gui.Cursor = GLCursor.Default; 79 | if (Click != null) 80 | Click(this, EventArgs.Empty); 81 | } 82 | } 83 | 84 | private void OnMouseEnter(object sender, EventArgs e) 85 | { 86 | Gui.Cursor = GLCursor.Hand; 87 | } 88 | 89 | private void OnMouseLeave(object sender, EventArgs e) 90 | { 91 | Gui.Cursor = GLCursor.Default; 92 | } 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLDraw.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Graphics; 4 | using OpenTK.Graphics.OpenGL; 5 | 6 | namespace GLGUI 7 | { 8 | public static class GLDraw 9 | { 10 | public static Rectangle CurrentScreenRect { get { return ControlRect; } } 11 | 12 | internal static GLGui CurrentGui; 13 | internal static Rectangle ControlRect; 14 | internal static Rectangle ScissorRect; 15 | 16 | public static void PrepareCustomDrawing() 17 | { 18 | GL.Scissor(ScissorRect.X, CurrentGui.Outer.Height - ScissorRect.Bottom, ScissorRect.Width, ScissorRect.Height); 19 | } 20 | 21 | public static void Fill(ref Color4 color) 22 | { 23 | if (color.A == 0.0f) 24 | return; 25 | 26 | GL.ClearColor(color); 27 | GL.Scissor(ScissorRect.X, CurrentGui.Outer.Height - ScissorRect.Bottom, ScissorRect.Width, ScissorRect.Height); 28 | GL.Clear(ClearBufferMask.ColorBufferBit); 29 | } 30 | 31 | public static void FillRect(Rectangle rect, ref Color4 color) 32 | { 33 | FillRect(ref rect, ref color); 34 | } 35 | 36 | public static void FillRect(ref Rectangle rect, ref Color4 color) 37 | { 38 | if (color.A == 0.0f) 39 | return; 40 | 41 | int w = Math.Min(rect.Width, ScissorRect.Width - rect.X); 42 | int h = Math.Min(rect.Height, ScissorRect.Height - rect.Y); 43 | if (w > 0 && h > 0) 44 | { 45 | GL.ClearColor(color); 46 | GL.Scissor(ScissorRect.X + rect.X, CurrentGui.Outer.Height - (ScissorRect.Y + rect.Y + h), w, h); 47 | GL.Clear(ClearBufferMask.ColorBufferBit); 48 | } 49 | } 50 | 51 | public static void Text(GLFontText processedText, Rectangle rect, ref Color4 color) 52 | { 53 | Text(processedText, ref rect, ref color); 54 | } 55 | 56 | public static void Text(GLFontText processedText, ref Rectangle rect, ref Color4 color) 57 | { 58 | if (color.A == 0.0f) 59 | return; 60 | 61 | int w = Math.Min(rect.Width, ScissorRect.Width - rect.X); 62 | int h = Math.Min(rect.Height, ScissorRect.Height - rect.Y); 63 | if (w > 0 && h > 0) 64 | { 65 | GL.Scissor(ScissorRect.X + rect.X, CurrentGui.Outer.Height - (ScissorRect.Y + rect.Y + h), w, h); 66 | GL.PushMatrix(); 67 | switch(processedText.alignment) 68 | { 69 | case GLFontAlignment.Left: 70 | case GLFontAlignment.Justify: 71 | GL.Translate(ControlRect.X + rect.X, ControlRect.Y + rect.Y, 0.0f); 72 | break; 73 | case GLFontAlignment.Centre: 74 | GL.Translate(ControlRect.X + rect.X + rect.Width / 2, ControlRect.Y + rect.Y, 0.0f); 75 | break; 76 | case GLFontAlignment.Right: 77 | GL.Translate(ControlRect.X + rect.X + rect.Width, ControlRect.Y + rect.Y, 0.0f); 78 | break; 79 | } 80 | GL.Color4(color); 81 | for (int i = 0; i < processedText.VertexBuffers.Length; i++) 82 | processedText.VertexBuffers[i].Draw(); 83 | GL.PopMatrix(); 84 | } 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontVertexBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using OpenTK.Graphics.OpenGL; 3 | 4 | namespace GLGUI 5 | { 6 | public class GLFontVertexBuffer : IDisposable 7 | { 8 | public int TextureID { get { return textureID; } internal set { textureID = value; } } 9 | public int VaoID { get { return vaoID; } } 10 | public int VertexCount { get { return vertexCount; } } 11 | 12 | int textureID; 13 | int vaoID, vboID; 14 | float[] vertices = new float[1024]; 15 | int floatCount = 0; 16 | int vertexCount = 0; 17 | 18 | public GLFontVertexBuffer(int textureID) 19 | { 20 | this.textureID = textureID; 21 | vaoID = GL.GenVertexArray(); 22 | vboID = GL.GenBuffer(); 23 | GLGui.usedVertexArrays++; 24 | } 25 | 26 | ~GLFontVertexBuffer() 27 | { 28 | lock(GLGui.toDispose) 29 | { 30 | GLGui.toDispose.Add(this); 31 | } 32 | } 33 | 34 | public void Dispose() 35 | { 36 | GL.DeleteVertexArray(vaoID); 37 | GL.DeleteBuffer(vboID); 38 | GLGui.usedVertexArrays--; 39 | } 40 | 41 | public void Reset() 42 | { 43 | floatCount = 0; 44 | vertexCount = 0; 45 | } 46 | 47 | public void AddQuad(float minx, float miny, float maxx, float maxy, float mintx, float minty, float maxtx, float maxty) 48 | { 49 | if (floatCount + 16 >= vertices.Length) 50 | Array.Resize(ref vertices, vertices.Length * 2); 51 | 52 | vertices[floatCount + 0] = minx; 53 | vertices[floatCount + 1] = miny; 54 | vertices[floatCount + 2] = mintx; 55 | vertices[floatCount + 3] = minty; 56 | 57 | vertices[floatCount + 4] = minx; 58 | vertices[floatCount + 5] = maxy; 59 | vertices[floatCount + 6] = mintx; 60 | vertices[floatCount + 7] = maxty; 61 | 62 | vertices[floatCount + 8] = maxx; 63 | vertices[floatCount + 9] = maxy; 64 | vertices[floatCount + 10] = maxtx; 65 | vertices[floatCount + 11] = maxty; 66 | 67 | vertices[floatCount + 12] = maxx; 68 | vertices[floatCount + 13] = miny; 69 | vertices[floatCount + 14] = maxtx; 70 | vertices[floatCount + 15] = minty; 71 | floatCount += 16; 72 | vertexCount += 4; 73 | } 74 | 75 | public void Load() 76 | { 77 | if (floatCount == 0) 78 | return; 79 | 80 | GL.BindVertexArray(vaoID); 81 | GL.BindBuffer(BufferTarget.ArrayBuffer, vboID); 82 | GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(floatCount * 4), vertices, BufferUsageHint.StaticDraw); 83 | 84 | GL.VertexPointer(2, VertexPointerType.Float, 16, 0); 85 | GL.EnableClientState(ArrayCap.VertexArray); 86 | GL.TexCoordPointer(2, TexCoordPointerType.Float, 16, 8); 87 | GL.EnableClientState(ArrayCap.TextureCoordArray); 88 | GL.BindVertexArray(0); 89 | } 90 | 91 | public void Draw() 92 | { 93 | if (vertexCount == 0) 94 | return; 95 | 96 | GL.Enable(EnableCap.Texture2D); 97 | GL.BindTexture(TextureTarget.Texture2D, textureID); 98 | 99 | GL.BindVertexArray(vaoID); 100 | GL.DrawArrays(PrimitiveType.Quads, 0, vertexCount); 101 | GL.BindVertexArray(0); 102 | 103 | GL.Disable(EnableCap.Texture2D); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GLGUI 5 | { 6 | class GLFontData 7 | { 8 | /// 9 | /// Mapping from a pair of characters to a pixel offset 10 | /// 11 | public Dictionary KerningPairs; 12 | 13 | /// 14 | /// List of texture pages 15 | /// 16 | public GLFontTexture[] Pages; 17 | 18 | /// 19 | /// Mapping from character to glyph index 20 | /// 21 | public Dictionary CharSetMapping; 22 | 23 | /// 24 | /// The average glyph width 25 | /// 26 | public float meanGlyphWidth; 27 | 28 | /// 29 | /// The maximum glyph height 30 | /// 31 | public int maxGlyphHeight; 32 | 33 | /// 34 | /// Whether the original font (from ttf) was detected to be monospaced 35 | /// 36 | public bool naturallyMonospaced = false; 37 | 38 | public bool IsMonospacingActive(GLFontRenderOptions options) 39 | { 40 | return (options.Monospacing == GLFontMonospacing.Natural && naturallyMonospaced) || options.Monospacing == GLFontMonospacing.Yes; 41 | } 42 | 43 | public float GetMonoSpaceWidth(GLFontRenderOptions options) 44 | { 45 | return (float)Math.Ceiling(1 + (1 + options.CharacterSpacing) * meanGlyphWidth); 46 | } 47 | 48 | public void CalculateMeanWidth() 49 | { 50 | meanGlyphWidth = 0f; 51 | foreach (var glyph in CharSetMapping) 52 | meanGlyphWidth += glyph.Value.Rect.Width; 53 | meanGlyphWidth /= CharSetMapping.Count; 54 | } 55 | 56 | public void CalculateMaxHeight() 57 | { 58 | maxGlyphHeight = 0; 59 | foreach (var glyph in CharSetMapping) 60 | maxGlyphHeight = Math.Max(glyph.Value.Rect.Height, maxGlyphHeight); 61 | } 62 | 63 | /// 64 | /// Returns the kerning length correction for the character at the given index in the given string. 65 | /// Also, if the text is part of a textNode list, the nextNode is given so that the following 66 | /// node can be checked incase of two adjacent word nodes. 67 | /// 68 | /// 69 | /// 70 | /// 71 | /// 72 | public int GetKerningPairCorrection(int index, string text, GLFontTextNode textNode) 73 | { 74 | if (KerningPairs == null) 75 | return 0; 76 | 77 | var chars = new char[2]; 78 | 79 | if (index + 1 == text.Length) 80 | { 81 | if (textNode != null && textNode.Next != null && textNode.Next.Type == GLFontTextNodeType.Word) 82 | chars[1] = textNode.Next.Text[0]; 83 | else 84 | return 0; 85 | } 86 | else 87 | { 88 | chars[1] = text[index + 1]; 89 | } 90 | 91 | chars[0] = text[index]; 92 | 93 | String str = new String(chars); 94 | 95 | if (KerningPairs.ContainsKey(str)) 96 | return KerningPairs[str]; 97 | 98 | return 0; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontKerningConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GLGUI 5 | { 6 | public class GLFontKerningConfiguration 7 | { 8 | /// 9 | /// Kerning rules for particular characters 10 | /// 11 | private Dictionary CharacterKerningRules = new Dictionary(); 12 | 13 | /// 14 | /// When measuring the bounds of glyphs, and performing kerning calculations, 15 | /// this is the minimum alpha level that is necessray for a pixel to be considered 16 | /// non-empty. This should be set to a value on the range [0,255] 17 | /// 18 | public byte AlphaEmptyPixelTolerance = 0; 19 | 20 | 21 | /// 22 | /// Sets all characters in the given string to the specified kerning rule. 23 | /// 24 | /// 25 | /// 26 | public void BatchSetCharacterKerningRule(String chars, GLFontCharacterKerningRule rule) 27 | { 28 | foreach (var c in chars) 29 | { 30 | CharacterKerningRules[c] = rule; 31 | } 32 | } 33 | 34 | /// 35 | /// Sets the specified character kerning rule. 36 | /// 37 | /// 38 | /// 39 | public void SetCharacterKerningRule(char c, GLFontCharacterKerningRule rule) 40 | { 41 | CharacterKerningRules[c] = rule; 42 | } 43 | 44 | public GLFontCharacterKerningRule GetCharacterKerningRule(char c) 45 | { 46 | if (CharacterKerningRules.ContainsKey(c)) 47 | { 48 | return CharacterKerningRules[c]; 49 | } 50 | 51 | return GLFontCharacterKerningRule.Normal; 52 | } 53 | 54 | /// 55 | /// Given a pair of characters, this will return the overriding 56 | /// CharacterKerningRule. 57 | /// 58 | /// 59 | /// 60 | public GLFontCharacterKerningRule GetOverridingCharacterKerningRuleForPair(String str) 61 | { 62 | if (str.Length < 2) 63 | { 64 | return GLFontCharacterKerningRule.Normal; 65 | } 66 | 67 | char c1 = str[0]; 68 | char c2 = str[1]; 69 | 70 | if (GetCharacterKerningRule(c1) == GLFontCharacterKerningRule.Zero || GetCharacterKerningRule(c2) == GLFontCharacterKerningRule.Zero) 71 | { 72 | return GLFontCharacterKerningRule.Zero; 73 | } 74 | else if (GetCharacterKerningRule(c1) == GLFontCharacterKerningRule.NotMoreThanHalf || GetCharacterKerningRule(c2) == GLFontCharacterKerningRule.NotMoreThanHalf) 75 | { 76 | return GLFontCharacterKerningRule.NotMoreThanHalf; 77 | } 78 | 79 | return GLFontCharacterKerningRule.Normal; 80 | } 81 | 82 | public GLFontKerningConfiguration() 83 | { 84 | SetCharacterKerningRule('^', GLFontCharacterKerningRule.NotMoreThanHalf); 85 | SetCharacterKerningRule('_', GLFontCharacterKerningRule.NotMoreThanHalf); 86 | SetCharacterKerningRule('\"', GLFontCharacterKerningRule.NotMoreThanHalf); 87 | SetCharacterKerningRule('\'', GLFontCharacterKerningRule.NotMoreThanHalf); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GLGUI.Example", "GLGUI.Example\GLGUI.Example.csproj", "{1A5AACE1-D7FD-4ECA-871D-95881F7D635B}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GLGUI", "GLGUI\GLGUI.csproj", "{D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GLGUI.GLControlExample", "GLGUI.GLControlExample\GLGUI.GLControlExample.csproj", "{BE449F2C-0E37-4D16-9E5B-D30D3041759B}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Debug|Mixed Platforms = Debug|Mixed Platforms 14 | Debug|x86 = Debug|x86 15 | Release|Any CPU = Release|Any CPU 16 | Release|Mixed Platforms = Release|Mixed Platforms 17 | Release|x86 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Debug|Any CPU.ActiveCfg = Debug|x86 21 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 22 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Debug|Mixed Platforms.Build.0 = Debug|x86 23 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Debug|x86.ActiveCfg = Debug|x86 24 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Debug|x86.Build.0 = Debug|x86 25 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Release|Any CPU.ActiveCfg = Release|x86 26 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Release|Mixed Platforms.ActiveCfg = Release|x86 27 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Release|Mixed Platforms.Build.0 = Release|x86 28 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Release|x86.ActiveCfg = Release|x86 29 | {1A5AACE1-D7FD-4ECA-871D-95881F7D635B}.Release|x86.Build.0 = Release|x86 30 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 33 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 34 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Debug|x86.ActiveCfg = Debug|Any CPU 35 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Debug|x86.Build.0 = Debug|Any CPU 36 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 39 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Release|Mixed Platforms.Build.0 = Release|Any CPU 40 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Release|x86.ActiveCfg = Release|Any CPU 41 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D}.Release|x86.Build.0 = Release|Any CPU 42 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Debug|Any CPU.ActiveCfg = Debug|x86 43 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 44 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Debug|Mixed Platforms.Build.0 = Debug|x86 45 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Debug|x86.ActiveCfg = Debug|x86 46 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Debug|x86.Build.0 = Debug|x86 47 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Release|Any CPU.ActiveCfg = Release|x86 48 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Release|Mixed Platforms.ActiveCfg = Release|x86 49 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Release|Mixed Platforms.Build.0 = Release|x86 50 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Release|x86.ActiveCfg = Release|x86 51 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B}.Release|x86.Build.0 = Release|x86 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(MonoDevelopProperties) = preSolution 57 | StartupItem = GLGUI.Example\GLGUI.Example.csproj 58 | EndGlobalSection 59 | EndGlobal 60 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/GLGUI.GLControlExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {BE449F2C-0E37-4D16-9E5B-D30D3041759B} 9 | WinExe 10 | Properties 11 | GLGUI.GLControlExample 12 | GLGUI.GLControlExample 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | ..\bin\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | x86 29 | pdbonly 30 | true 31 | ..\bin\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\bin\OpenTK.dll 39 | 40 | 41 | ..\bin\OpenTK.GLControl.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | Form 51 | 52 | 53 | 54 | UserControl 55 | 56 | 57 | 58 | 59 | ResXFileCodeGenerator 60 | Resources.Designer.cs 61 | Designer 62 | 63 | 64 | True 65 | Resources.resx 66 | 67 | 68 | SettingsSingleFileGenerator 69 | Settings.Designer.cs 70 | 71 | 72 | True 73 | Settings.settings 74 | True 75 | 76 | 77 | 78 | 79 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D} 80 | GLGUI 81 | 82 | 83 | 84 | 91 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Input; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLButton : GLControl 8 | { 9 | public string Text { get { return text; } set { if (value != text) { text = value; Invalidate(); } } } 10 | public bool Enabled { get { return enabled; } set { enabled = value; Invalidate(); } } 11 | public GLSkin.GLButtonSkin SkinEnabled { get { return skinEnabled; } set { skinEnabled = value; Invalidate(); } } 12 | public GLSkin.GLButtonSkin SkinPressed { get { return skinPressed; } set { skinPressed = value; Invalidate(); } } 13 | public GLSkin.GLButtonSkin SkinHover { get { return skinHover; } set { skinHover = value; Invalidate(); } } 14 | public GLSkin.GLButtonSkin SkinDisabled { get { return skinDisabled; } set { skinDisabled = value; Invalidate(); } } 15 | 16 | public event EventHandler Click; 17 | 18 | private string text = ""; 19 | private GLFontText textProcessed = new GLFontText(); 20 | private SizeF textSize; 21 | private GLSkin.GLButtonSkin skinEnabled, skinPressed, skinHover, skinDisabled; 22 | private GLSkin.GLButtonSkin skin; 23 | private bool enabled = true; 24 | private bool down = false; 25 | private bool over = false; 26 | 27 | public GLButton(GLGui gui) : base(gui) 28 | { 29 | Render += OnRender; 30 | MouseDown += OnMouseDown; 31 | MouseUp += OnMouseUp; 32 | MouseEnter += OnMouseEnter; 33 | MouseLeave += OnMouseLeave; 34 | 35 | skinEnabled = Gui.Skin.ButtonEnabled; 36 | skinPressed = Gui.Skin.ButtonPressed; 37 | skinHover = Gui.Skin.ButtonHover; 38 | skinDisabled = Gui.Skin.ButtonDisabled; 39 | 40 | outer = new Rectangle(0, 0, 75, 0); 41 | sizeMin = new Size(8, 8); 42 | sizeMax = new Size(int.MaxValue, int.MaxValue); 43 | } 44 | 45 | protected override void UpdateLayout() 46 | { 47 | skin = Enabled ? (down ? skinPressed : (over ? skinHover : skinEnabled)) : skinDisabled; 48 | 49 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 50 | 51 | int innerWidth = outer.Width - skin.Border.Horizontal; 52 | textSize = skin.Font.ProcessText(textProcessed, text, skin.TextAlign); 53 | int minHeight = Math.Max(sizeMin.Height, (int)textSize.Height + skin.Border.Vertical); 54 | 55 | if (AutoSize) 56 | { 57 | innerWidth = (int)textSize.Width; 58 | outer.Width = innerWidth + skin.Border.Horizontal; 59 | outer.Height = minHeight; 60 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 61 | } 62 | 63 | outer.Height = Math.Min(Math.Max(outer.Height, minHeight), sizeMax.Height); 64 | 65 | Inner = new Rectangle( 66 | skin.Border.Left, skin.Border.Top, 67 | innerWidth, outer.Height - skin.Border.Vertical); 68 | } 69 | 70 | private void OnRender(object sender, double timeDelta) 71 | { 72 | GLDraw.Fill(ref skin.BorderColor); 73 | GLDraw.FillRect(Inner, ref skin.BackgroundColor); 74 | GLDraw.Text(textProcessed, Inner, ref skin.Color); 75 | } 76 | 77 | private void OnMouseDown(object sender, MouseButtonEventArgs e) 78 | { 79 | if (enabled && e.Button == MouseButton.Left) 80 | { 81 | isDragged = true; 82 | down = true; 83 | Invalidate(); 84 | } 85 | } 86 | 87 | private void OnMouseUp(object sender, MouseButtonEventArgs e) 88 | { 89 | if (enabled && e.Button == MouseButton.Left) 90 | { 91 | if (down) 92 | { 93 | down = false; 94 | isDragged = false; 95 | if(Click != null && new Rectangle(0, 0, outer.Width, outer.Height).Contains(e.Position)) 96 | Click(this, e); 97 | Invalidate(); 98 | } 99 | } 100 | } 101 | 102 | private void OnMouseEnter(object sender, EventArgs e) 103 | { 104 | over = true; 105 | Invalidate(); 106 | } 107 | 108 | private void OnMouseLeave(object sender, EventArgs e) 109 | { 110 | over = false; 111 | Invalidate(); 112 | } 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFlowLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLFlowLayout : GLControl 8 | { 9 | public GLFlowDirection FlowDirection { get { return flowDirection; } set { flowDirection = value; Invalidate(); } } 10 | public GLSkin.GLFlowLayoutSkin Skin { get { return skin; } set { skin = value; Invalidate(); } } 11 | 12 | private GLFlowDirection flowDirection = GLFlowDirection.LeftToRight; 13 | private GLSkin.GLFlowLayoutSkin skin; 14 | private Rectangle background; 15 | 16 | public GLFlowLayout(GLGui gui) : base(gui) 17 | { 18 | Render += OnRender; 19 | 20 | skin = Gui.Skin.FlowLayout; 21 | 22 | outer = new Rectangle(0, 0, 100, 100); 23 | sizeMin = new Size(0, 0); 24 | sizeMax = new Size(int.MaxValue, int.MaxValue); 25 | } 26 | 27 | private void Invalidate(object sender, EventArgs e) 28 | { 29 | Invalidate(); 30 | } 31 | 32 | private void UpdatePositions() 33 | { 34 | int current = 0; 35 | switch(FlowDirection) 36 | { 37 | case GLFlowDirection.LeftToRight: 38 | foreach(GLControl control in Controls) 39 | { 40 | Rectangle o = control.Outer; 41 | control.Outer = new Rectangle(current, 0, o.Width, o.Height); 42 | current += o.Width + skin.Space; 43 | } 44 | break; 45 | case GLFlowDirection.RightToLeft: 46 | current = Inner.Width; 47 | foreach(GLControl control in Controls) 48 | { 49 | Rectangle o = control.Outer; 50 | current -= o.Width; 51 | control.Outer = new Rectangle(current, 0, o.Width, o.Height); 52 | current -= skin.Space; 53 | } 54 | break; 55 | case GLFlowDirection.TopDown: 56 | foreach(GLControl control in Controls) 57 | { 58 | Rectangle o = control.Outer; 59 | control.Outer = new Rectangle(0, current, o.Width, o.Height); 60 | current += o.Height + skin.Space; 61 | } 62 | break; 63 | case GLFlowDirection.BottomUp: 64 | current = Inner.Height; 65 | foreach(GLControl control in Controls) 66 | { 67 | Rectangle o = control.Outer; 68 | current -= o.Height; 69 | control.Outer = new Rectangle(0, current, o.Width, o.Height); 70 | current -= skin.Space; 71 | } 72 | break; 73 | } 74 | } 75 | 76 | protected override void UpdateLayout() 77 | { 78 | UpdatePositions(); 79 | 80 | if (AutoSize) 81 | { 82 | if (Controls.Count() > 0) 83 | { 84 | outer.Width = Controls.Max(c => c.Outer.Right) - Controls.Min(c => c.Outer.Left) + skin.Padding.Horizontal + skin.Border.Horizontal; 85 | outer.Height = Controls.Max(c => c.Outer.Bottom) - Controls.Min(c => c.Outer.Top) + skin.Padding.Vertical + skin.Border.Vertical; 86 | } 87 | else 88 | { 89 | outer.Width = skin.Padding.Horizontal + skin.Border.Horizontal; 90 | outer.Height = skin.Padding.Vertical + skin.Border.Vertical; 91 | } 92 | } 93 | 94 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 95 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 96 | background = new Rectangle( 97 | skin.Border.Left, skin.Border.Top, 98 | outer.Width - skin.Border.Horizontal, outer.Height - skin.Border.Vertical); 99 | Inner = new Rectangle( 100 | background.Left + skin.Padding.Left, background.Top + skin.Padding.Top, 101 | background.Width - skin.Padding.Horizontal, background.Height - skin.Padding.Vertical); 102 | 103 | if(flowDirection == GLFlowDirection.BottomUp || flowDirection == GLFlowDirection.RightToLeft) 104 | UpdatePositions(); 105 | } 106 | 107 | private void OnRender(object sender, double timeDelta) 108 | { 109 | GLDraw.Fill(ref skin.BorderColor); 110 | GLDraw.FillRect(ref background, ref skin.BackgroundColor); 111 | } 112 | 113 | public override T Add(T control) 114 | { 115 | base.Add(control); 116 | control.Resize += Invalidate; 117 | Invalidate(); 118 | return control; 119 | } 120 | 121 | public override void Remove(GLControl control) 122 | { 123 | base.Remove(control); 124 | control.Resize -= Invalidate; 125 | Invalidate(); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLSplitLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Input; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLSplitLayout : GLControl 8 | { 9 | public GLSplitterOrientation Orientation { get { return orientation; } set { orientation = value; Invalidate(); } } 10 | public float SplitterPosition { get { return splitterPosition; } set { splitterPosition = value; Invalidate(); } } 11 | public GLSkin.GLSplitLayoutSkin Skin { get { return skin; } set { skin = value; Invalidate(); } } 12 | public GLControl First { get { return first; } } 13 | public GLControl Second { get { return second; } } 14 | 15 | private GLSplitterOrientation orientation = GLSplitterOrientation.Vertical; 16 | private GLSkin.GLSplitLayoutSkin skin; 17 | private GLControl first, second; 18 | private float splitterPosition = 0.5f; 19 | 20 | public GLSplitLayout(GLGui gui) : base(gui) 21 | { 22 | MouseDown += OnMouseDown; 23 | MouseUp += OnMouseUp; 24 | MouseMove += OnMouseMove; 25 | Render += OnRender; 26 | 27 | skin = Gui.Skin.SplitLayout; 28 | 29 | outer = new Rectangle(0, 0, 100, 100); 30 | sizeMin = new Size(0, 0); 31 | sizeMax = new Size(int.MaxValue, int.MaxValue); 32 | } 33 | 34 | protected override void UpdateLayout() 35 | { 36 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 37 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 38 | Inner = new Rectangle(0, 0, outer.Width, outer.Height); 39 | 40 | splitterPosition = Math.Min(Math.Max(splitterPosition, 0.0f), 1.0f); 41 | if (orientation == GLSplitterOrientation.Vertical) 42 | { 43 | int splitter = (int)((Inner.Width - skin.SplitterSize) * splitterPosition); 44 | if (first != null) 45 | first.Outer = new Rectangle(0, 0, splitter, Inner.Height); 46 | if (second != null) 47 | second.Outer = new Rectangle(splitter + skin.SplitterSize, 0, Inner.Width - splitter - skin.SplitterSize, Inner.Height); 48 | splitterRect = new Rectangle(splitter, 0, skin.SplitterSize, Inner.Height); 49 | } 50 | else 51 | { 52 | int splitter = (int)((Inner.Height - skin.SplitterSize) * splitterPosition); 53 | if (first != null) 54 | first.Outer = new Rectangle(0, 0, Inner.Width, splitter); 55 | if (second != null) 56 | second.Outer = new Rectangle(0, splitter + skin.SplitterSize, Inner.Width, Inner.Height - splitter - skin.SplitterSize); 57 | splitterRect = new Rectangle(0, splitter, Inner.Width, skin.SplitterSize); 58 | } 59 | } 60 | 61 | private Rectangle splitterRect; 62 | private void OnRender(object sender, double timeDelta) 63 | { 64 | GLDraw.FillRect(ref splitterRect, ref skin.BackgroundColor); 65 | } 66 | 67 | public override T Add(T control) 68 | { 69 | if (first != null && second != null) 70 | { 71 | string message = string.Format("{0} {1} already has two children.", 72 | control.GetType().Name, control.Name); 73 | throw new InvalidOperationException(message); 74 | } 75 | base.Add(control); 76 | if (first == null) 77 | first = control; 78 | else 79 | second = control; 80 | Invalidate(); 81 | return control; 82 | } 83 | 84 | public override void Remove(GLControl control) 85 | { 86 | if (first == control) 87 | first = null; 88 | if (second == control) 89 | second = null; 90 | base.Remove(control); 91 | } 92 | 93 | private void OnMouseDown(object sender, MouseButtonEventArgs e) 94 | { 95 | if (e.Button == MouseButton.Left) 96 | { 97 | isDragged = true; 98 | } 99 | } 100 | 101 | private void OnMouseUp(object sender, MouseButtonEventArgs e) 102 | { 103 | if (e.Button == MouseButton.Left) 104 | { 105 | if (isDragged) 106 | { 107 | isDragged = false; 108 | Gui.Cursor = GLCursor.Default; 109 | } 110 | } 111 | } 112 | 113 | private void OnMouseMove(object sender, MouseEventArgs e) 114 | { 115 | if (isDragged) 116 | { 117 | if (orientation == GLSplitterOrientation.Vertical) 118 | splitterPosition = (float)(e.X - skin.SplitterSize / 2) / (float)Inner.Width; 119 | else 120 | splitterPosition = (float)(e.Y - skin.SplitterSize / 2) / (float)Inner.Height; 121 | Invalidate(); 122 | } 123 | Gui.Cursor = orientation == GLSplitterOrientation.Horizontal ? GLCursor.SizeNS : GLCursor.SizeWE; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLCursor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.Runtime.InteropServices; 5 | using System.Security; 6 | using OpenTK; 7 | 8 | namespace GLGUI 9 | { 10 | public class GLCursor 11 | { 12 | public static GLCursor Default, SizeAll, SizeNWSE, SizeNS, SizeNESW, SizeWE, IBeam, Hand, None; 13 | 14 | internal readonly IntPtr Handle; 15 | #if REFERENCE_WINDOWS_FORMS 16 | internal readonly System.Windows.Forms.Cursor Cursor; 17 | #endif 18 | 19 | private static bool loaded = false; 20 | private static bool usingSdl2 = false; 21 | 22 | #if REFERENCE_WINDOWS_FORMS 23 | private GLCursor() 24 | { 25 | if (usingSdl2) 26 | { 27 | byte[] dummy = new byte[4 * 32]; 28 | unsafe 29 | { 30 | fixed(byte* d = dummy) 31 | { 32 | Handle = CreateCursor((IntPtr)d, (IntPtr)d, 32, 32, 0, 0); 33 | } 34 | } 35 | } 36 | else 37 | { 38 | Bitmap bmp = new Bitmap(1, 1); 39 | Handle = bmp.GetHicon(); 40 | bmp.Dispose(); 41 | Cursor = new System.Windows.Forms.Cursor(Handle); 42 | } 43 | } 44 | 45 | private GLCursor(System.Windows.Forms.Cursor cursor, bool centeredHotSpot = true) 46 | { 47 | if (!usingSdl2) 48 | { 49 | Handle = cursor.Handle; 50 | Cursor = cursor; 51 | return; 52 | } 53 | 54 | // now it's getting ugly... 55 | // this is converting our shiny cursors to the minimalistic cursor support of sdl2. 56 | // since there seems to be NO F*CKING WAY to get the cursor hot spot from X11, 57 | // we just center it for every cursor except the default pointer and the hand. 58 | int w = Math.Max(cursor.Size.Width, 32); // also, no size... 59 | int h = Math.Max(cursor.Size.Height, 32); // (╯°□°)╯︵ ┻━┻ 60 | var rect = new Rectangle(0, 0, w, h); 61 | 62 | var bmp = new Bitmap(w, h); 63 | bmp.MakeTransparent(); 64 | using(var g = Graphics.FromImage(bmp)) 65 | { 66 | cursor.Draw(g, rect); 67 | } 68 | var data = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 69 | var color = new byte[data.Height * data.Width / 8]; 70 | var visibility = new byte[data.Height * data.Width / 8]; 71 | 72 | unsafe 73 | { 74 | fixed(byte* c = color, v = visibility) 75 | { 76 | Point min = new Point(int.MaxValue, int.MaxValue), max = new Point(); 77 | byte* d = (byte*)data.Scan0; 78 | for(int y = 0; y < data.Height; y++) 79 | { 80 | for(int x = 0; x < data.Width; x++) 81 | { 82 | int a = d[y * data.Stride + x * 4 + 3]; 83 | int l = d[y * data.Stride + x * 4 + 0] + d[y * data.Stride + x * 4 + 1] + d[y * data.Stride + x * 4 + 2]; 84 | if (a > 100) 85 | { 86 | min.X = Math.Min(min.X, x); min.Y = Math.Min(min.Y, y); 87 | max.X = Math.Max(max.X, x); max.Y = Math.Max(max.Y, y); 88 | 89 | v[(y * data.Width + x) / 8] |= (byte)(1 << (7 - (x & 7))); 90 | if (l < 300) 91 | c[(y * data.Width + x) / 8] |= (byte)(1 << (7 - (x & 7))); 92 | } 93 | } 94 | } 95 | Point hotspot = centeredHotSpot ? new Point((min.X + max.X) / 2, (min.Y + max.Y) / 2) : min; 96 | Handle = CreateCursor((IntPtr)c, (IntPtr)v, data.Width, data.Height, hotspot.X, hotspot.Y); 97 | } 98 | } 99 | bmp.UnlockBits(data); 100 | bmp.Dispose(); 101 | } 102 | #endif 103 | 104 | internal static void LoadCursors(NativeWindow window) 105 | { 106 | if (loaded) 107 | return; 108 | 109 | if (window != null) 110 | { 111 | string infoType = window.WindowInfo.GetType().Name; 112 | if (infoType == "Sdl2WindowInfo") 113 | usingSdl2 = true; 114 | } 115 | 116 | #if REFERENCE_WINDOWS_FORMS 117 | Default = new GLCursor(System.Windows.Forms.Cursors.Default, false); 118 | SizeAll = new GLCursor(System.Windows.Forms.Cursors.SizeAll); 119 | SizeNWSE = new GLCursor(System.Windows.Forms.Cursors.SizeNWSE); 120 | SizeNS = new GLCursor(System.Windows.Forms.Cursors.SizeNS); 121 | SizeNESW = new GLCursor(System.Windows.Forms.Cursors.SizeNESW); 122 | SizeWE = new GLCursor(System.Windows.Forms.Cursors.SizeWE); 123 | IBeam = new GLCursor(System.Windows.Forms.Cursors.IBeam); 124 | Hand = new GLCursor(System.Windows.Forms.Cursors.Hand, false); 125 | None = new GLCursor(); 126 | #endif 127 | 128 | loaded = true; 129 | } 130 | 131 | [SuppressUnmanagedCodeSecurity] 132 | [DllImport("SDL2.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "SDL_CreateCursor", ExactSpelling = true)] 133 | private static extern IntPtr CreateCursor(IntPtr data, IntPtr mask, int w, int h, int hot_x, int hot_y); 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLCheckBox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Input; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLCheckBox : GLControl 8 | { 9 | public string Text { get { return text; } set { if (value != text) { text = value; Invalidate(); } } } 10 | public bool Enabled { get { return enabled; } set { enabled = value; Invalidate(); } } 11 | public bool Checked { get { return _checked; } set { _checked = value; Invalidate(); } } 12 | public GLSkin.GLCheckBoxSkin SkinEnabled { get { return skinEnabled; } set { skinEnabled = value; Invalidate(); } } 13 | public GLSkin.GLCheckBoxSkin SkinPressed { get { return skinPressed; } set { skinPressed = value; Invalidate(); } } 14 | public GLSkin.GLCheckBoxSkin SkinHover { get { return skinHover; } set { skinHover = value; Invalidate(); } } 15 | public GLSkin.GLCheckBoxSkin SkinDisabled { get { return skinDisabled; } set { skinDisabled = value; Invalidate(); } } 16 | 17 | public event EventHandler Changed; 18 | 19 | private string text = ""; 20 | private GLFontText textProcessed = new GLFontText(); 21 | private SizeF textSize; 22 | private GLSkin.GLCheckBoxSkin skinEnabled, skinPressed, skinHover, skinDisabled; 23 | private GLSkin.GLCheckBoxSkin skin; 24 | private bool enabled = true; 25 | private bool _checked = false; 26 | private bool down = false; 27 | private bool over = false; 28 | private Rectangle outerBox = new Rectangle(0, 0, 10, 10); 29 | private Rectangle innerBox; 30 | 31 | public GLCheckBox(GLGui gui) : base(gui) 32 | { 33 | Render += OnRender; 34 | MouseDown += OnMouseDown; 35 | MouseUp += OnMouseUp; 36 | MouseEnter += OnMouseEnter; 37 | MouseLeave += OnMouseLeave; 38 | 39 | skinEnabled = Gui.Skin.CheckBoxEnabled; 40 | skinPressed = Gui.Skin.CheckBoxPressed; 41 | skinHover = Gui.Skin.CheckBoxHover; 42 | skinDisabled = Gui.Skin.CheckBoxDisabled; 43 | 44 | outer = outerBox; 45 | sizeMin = outerBox.Size; 46 | sizeMax = new Size(int.MaxValue, int.MaxValue); 47 | 48 | HandleMouseEvents = false; 49 | } 50 | 51 | protected override void UpdateLayout() 52 | { 53 | skin = Enabled ? (down ? skinPressed : (over ? skinHover : skinEnabled)) : skinDisabled; 54 | 55 | innerBox = new Rectangle(skin.Border.Left, skin.Border.Top, 56 | outerBox.Width - skin.Border.Horizontal, outerBox.Height - skin.Border.Vertical); 57 | 58 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 59 | 60 | int innerWidth = outer.Width - skin.Border.Horizontal - outerBox.Width; 61 | textSize = skin.Font.ProcessText(textProcessed, text, GLFontAlignment.Left); 62 | int minHeight = Math.Max(sizeMin.Height, (int)textSize.Height + skin.Border.Vertical); 63 | 64 | if (AutoSize) 65 | { 66 | innerWidth = (int)textSize.Width; 67 | outer.Width = innerWidth + outerBox.Width + skin.Border.Horizontal; 68 | outer.Height = minHeight; 69 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 70 | } 71 | 72 | outer.Height = Math.Min(Math.Max(outer.Height, minHeight), sizeMax.Height); 73 | 74 | Inner = new Rectangle( 75 | outerBox.Right + skin.Border.Left, skin.Border.Top, 76 | innerWidth, outer.Height - skin.Border.Vertical); 77 | } 78 | 79 | private void OnRender(object sender, double timeDelta) 80 | { 81 | GLDraw.FillRect(ref outerBox, ref skin.BorderColor); 82 | GLDraw.FillRect(ref innerBox, ref skin.BackgroundColor); 83 | if (_checked) 84 | GLDraw.FillRect(new Rectangle(innerBox.X + skin.Border.Left, innerBox.Y + skin.Border.Top, 85 | innerBox.Width - skin.Border.Horizontal, innerBox.Height - skin.Border.Vertical), ref skin.BorderColor); 86 | GLDraw.Text(textProcessed, Inner, ref skin.Color); 87 | } 88 | 89 | private void OnMouseDown(object sender, MouseButtonEventArgs e) 90 | { 91 | if (enabled && e.Button == MouseButton.Left) 92 | { 93 | isDragged = true; 94 | down = true; 95 | Invalidate(); 96 | } 97 | } 98 | 99 | private void OnMouseUp(object sender, MouseButtonEventArgs e) 100 | { 101 | if (enabled && e.Button == MouseButton.Left) 102 | { 103 | if (down) 104 | { 105 | down = false; 106 | isDragged = false; 107 | _checked = !_checked; 108 | if(Changed != null) 109 | Changed(this, e); 110 | Invalidate(); 111 | } 112 | } 113 | } 114 | 115 | private void OnMouseEnter(object sender, EventArgs e) 116 | { 117 | over = true; 118 | Invalidate(); 119 | } 120 | 121 | private void OnMouseLeave(object sender, EventArgs e) 122 | { 123 | over = false; 124 | Invalidate(); 125 | } 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontRenderOptions.cs: -------------------------------------------------------------------------------- 1 | namespace GLGUI 2 | { 3 | public class GLFontRenderOptions 4 | { 5 | /// 6 | /// Spacing between characters in units of average glyph width 7 | /// 8 | public float CharacterSpacing = 0.05f; 9 | 10 | /// 11 | /// Spacing between words in units of average glyph width 12 | /// 13 | public float WordSpacing = 0.6f; 14 | 15 | /// 16 | /// Line spacing in units of max glyph width 17 | /// 18 | public float LineSpacing = 1.0f; 19 | 20 | /// 21 | /// Whether to render the font in monospaced mode. If set to "Natural", then 22 | /// monospacing will be used if the font loaded font was detected to be monospaced. 23 | /// 24 | public GLFontMonospacing Monospacing = GLFontMonospacing.Natural; 25 | 26 | /// 27 | /// Locks the position to a particular pixel, allowing the text to be rendered pixel-perfectly. 28 | /// You need to turn this off if you wish to move text around the screen smoothly by fractions 29 | /// of a pixel. 30 | /// 31 | public bool LockToPixel; 32 | 33 | /// 34 | /// Only applies when LockToPixel is true: 35 | /// This is used to transition smoothly between being locked to pixels and not 36 | /// 37 | public float LockToPixelRatio = 1.0f; 38 | 39 | /// 40 | /// Wrap word to next line if max width hit 41 | /// 42 | public bool WordWrap = true; 43 | 44 | 45 | #region Justify Options 46 | 47 | /// 48 | /// When a line of text is justified, space may be inserted between 49 | /// characters, and between words. 50 | /// 51 | /// This parameter determines how this choice is weighted: 52 | /// 53 | /// 0.0f => word spacing only 54 | /// 1.0f => "fairly" distributed between both 55 | /// > 1.0 => in favour of character spacing 56 | /// 57 | /// This applies to expansions only. 58 | /// 59 | /// 60 | public float JustifyCharacterWeightForExpand 61 | { 62 | get { return justifyCharWeightForExpand; } 63 | set 64 | { 65 | justifyCharWeightForExpand = value; 66 | if (justifyCharWeightForExpand < 0f) 67 | justifyCharWeightForExpand = 0f; 68 | else if (justifyCharWeightForExpand > 1.0f) 69 | justifyCharWeightForExpand = 1.0f; 70 | } 71 | } 72 | 73 | private float justifyCharWeightForExpand = 0.5f; 74 | 75 | /// 76 | /// When a line of text is justified, space may be removed between 77 | /// characters, and between words. 78 | /// 79 | /// This parameter determines how this choice is weighted: 80 | /// 81 | /// 0.0f => word spacing only 82 | /// 1.0f => "fairly" distributed between both 83 | /// > 1.0 => in favour of character spacing 84 | /// 85 | /// This applies to contractions only. 86 | /// 87 | /// 88 | public float JustifyCharacterWeightForContract 89 | { 90 | get { return justifyCharWeightForContract; } 91 | set 92 | { 93 | justifyCharWeightForContract = value; 94 | if (justifyCharWeightForContract < 0f) 95 | justifyCharWeightForContract = 0f; 96 | else if (justifyCharWeightForContract > 1.0f) 97 | justifyCharWeightForContract = 1.0f; 98 | } 99 | } 100 | 101 | private float justifyCharWeightForContract = 0.2f; 102 | 103 | /// 104 | /// Total justification cap as a fraction of the boundary width. 105 | /// 106 | public float JustifyCapExpand = 0.5f; 107 | 108 | /// 109 | /// Total justification cap as a fraction of the boundary width. 110 | /// 111 | public float JustifyCapContract = 0.1f; 112 | 113 | /// 114 | /// By what factor justification is penalized for being negative. 115 | /// 116 | /// (e.g. if this is set to 3, then a contraction will only happen 117 | /// over an expansion if it is 3 of fewer times smaller than the 118 | /// expansion). 119 | /// 120 | /// 121 | /// 122 | public float JustifyContractionPenalty = 2; 123 | 124 | #endregion 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLGUI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 10.0.0 7 | 2.0 8 | {D0281455-4FAF-4AF6-ABE4-8F9E7A25828D} 9 | Library 10 | GLGUI 11 | GLGUI 12 | 13 | 14 | true 15 | full 16 | false 17 | ..\bin 18 | DEBUG;REFERENCE_WINDOWS_FORMS; REFERENCE_OPENTK_GLCONTROL 19 | prompt 20 | 4 21 | false 22 | true 23 | 24 | 25 | true 26 | ..\bin 27 | prompt 28 | 4 29 | false 30 | true 31 | REFERENCE_WINDOWS_FORMS; REFERENCE_OPENTK_GLCONTROL 32 | 33 | 34 | 35 | False 36 | ..\bin\OpenTK.GLControl.dll 37 | 38 | 39 | 40 | ..\bin\OpenTK.dll 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLSlider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK.Input; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLSlider : GLControl 8 | { 9 | public GLSliderOrientation Direction { get { return direction; } set { direction = value; Invalidate(); } } 10 | public float Value { get { return value; } set { this.value = value; Invalidate(); if (ValueChanged != null) ValueChanged(this, EventArgs.Empty); } } 11 | public bool Enabled { get { return enabled; } set { enabled = value; Invalidate(); } } 12 | public GLSkin.GLSliderSkin SkinEnabled { get { return skinEnabled; } set { skinEnabled = value; Invalidate(); } } 13 | public GLSkin.GLSliderSkin SkinPressed { get { return skinPressed; } set { skinPressed = value; Invalidate(); } } 14 | public GLSkin.GLSliderSkin SkinHover { get { return skinHover; } set { skinHover = value; Invalidate(); } } 15 | public GLSkin.GLSliderSkin SkinDisabled { get { return skinDisabled; } set { skinDisabled = value; Invalidate(); } } 16 | public float MouseWheelStep = 0.001f; 17 | 18 | public event EventHandler ValueChanged; 19 | 20 | private GLSkin.GLSliderSkin skinEnabled, skinPressed, skinHover, skinDisabled; 21 | private GLSkin.GLSliderSkin skin; 22 | private GLSliderOrientation direction = GLSliderOrientation.Vertical; 23 | private float value = 0.0f; 24 | private bool enabled = true; 25 | private bool down = false; 26 | private bool over = false; 27 | 28 | public GLSlider(GLGui gui) : base(gui) 29 | { 30 | Render += OnRender; 31 | MouseMove += OnMouseMove; 32 | MouseDown += OnMouseDown; 33 | MouseUp += OnMouseUp; 34 | MouseEnter += OnMouseEnter; 35 | MouseLeave += OnMouseLeave; 36 | MouseWheel += OnMouseWheel; 37 | 38 | skinEnabled = Gui.Skin.SliderEnabled; 39 | skinPressed = Gui.Skin.SliderPressed; 40 | skinHover = Gui.Skin.SliderHover; 41 | skinDisabled = Gui.Skin.SliderDisabled; 42 | 43 | outer = new Rectangle(0, 0, 8, 8); 44 | sizeMin = new Size(8, 8); 45 | sizeMax = new Size(int.MaxValue, int.MaxValue); 46 | } 47 | 48 | protected override void UpdateLayout() 49 | { 50 | skin = Enabled ? (down ? skinPressed : (over ? skinHover : skinEnabled)) : skinDisabled; 51 | 52 | if (AutoSize) 53 | { 54 | outer.Width = sizeMin.Width; 55 | outer.Height = sizeMin.Height; 56 | if (Parent != null) 57 | { 58 | if (direction == GLSliderOrientation.Horizontal) 59 | outer.Width = Parent.Inner.Width; 60 | else 61 | outer.Height = Parent.Inner.Height; 62 | } 63 | } 64 | 65 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 66 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 67 | 68 | if (!enabled) 69 | value = 0.0f; 70 | value = Math.Min(Math.Max(value, 0.0f), 1.0f); 71 | if (direction == GLSliderOrientation.Horizontal) 72 | Inner = new Rectangle((int)((outer.Width - 8) * value), 0, 8, 8); 73 | else 74 | Inner = new Rectangle(0, (int)((outer.Height - 8) * value), 8, 8); 75 | } 76 | 77 | private void OnRender(object sender, double timeDelta) 78 | { 79 | GLDraw.Fill(ref skin.BackgroundColor); 80 | GLDraw.FillRect(Inner, ref skin.KnobColor); 81 | } 82 | 83 | private void OnMouseMove(object sender, MouseMoveEventArgs e) 84 | { 85 | if (isDragged && enabled) 86 | { 87 | if (direction == GLSliderOrientation.Horizontal) 88 | Value = (float)(e.X - 4) / (float)(outer.Width - 8); 89 | else 90 | Value = (float)(e.Y - 4) / (float)(outer.Height - 8); 91 | } 92 | } 93 | 94 | private void OnMouseDown(object sender, MouseButtonEventArgs e) 95 | { 96 | if (e.Button == MouseButton.Left && enabled) 97 | { 98 | isDragged = true; 99 | down = true; 100 | if (direction == GLSliderOrientation.Horizontal) 101 | Value = (float)(e.X - 4) / (float)(outer.Width - 8); 102 | else 103 | Value = (float)(e.Y - 4) / (float)(outer.Height - 8); 104 | } 105 | } 106 | 107 | private void OnMouseUp(object sender, MouseButtonEventArgs e) 108 | { 109 | if (e.Button == MouseButton.Left && enabled) 110 | { 111 | if (down) 112 | { 113 | down = false; 114 | isDragged = false; 115 | Invalidate(); 116 | } 117 | } 118 | } 119 | 120 | private void OnMouseEnter(object sender, EventArgs e) 121 | { 122 | over = true; 123 | Invalidate(); 124 | } 125 | 126 | private void OnMouseLeave(object sender, EventArgs e) 127 | { 128 | over = false; 129 | Invalidate(); 130 | } 131 | 132 | private void OnMouseWheel(object sender, MouseWheelEventArgs e) 133 | { 134 | if (Enabled) 135 | Value += -e.Delta * MouseWheelStep; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontKerningCalculator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace GLGUI 5 | { 6 | class GLFontKerningCalculator 7 | { 8 | private struct XLimits 9 | { 10 | public int Min; 11 | public int Max; 12 | } 13 | 14 | private static int Kerning(GLFontGlyph g1, GLFontGlyph g2, XLimits[] lim1, XLimits[] lim2, GLFontKerningConfiguration config) 15 | { 16 | int yOffset1 = g1.YOffset; 17 | int yOffset2 = g2.YOffset; 18 | 19 | int startY = Math.Max(yOffset1, yOffset2); 20 | int endY = Math.Min(g1.Rect.Height + yOffset1, g2.Rect.Height + yOffset2); 21 | 22 | int w1 = g1.Rect.Width; 23 | 24 | int worstCase = w1; 25 | 26 | //TODO - offset startY, endY by yOffset1 so that lim1[j-yOffset1] can be written as lim1[j], will need another var for yOffset2 27 | 28 | for (int j = startY; j < endY; j++) 29 | worstCase = Math.Min(worstCase, w1 - lim1[j-yOffset1].Max + lim2[j-yOffset2].Min); 30 | 31 | worstCase = Math.Min(worstCase, g1.Rect.Width); 32 | worstCase = Math.Min(worstCase, g2.Rect.Width); 33 | 34 | //modify by character kerning rules 35 | GLFontCharacterKerningRule kerningRule = config.GetOverridingCharacterKerningRuleForPair(""+g1.Character + g2.Character); 36 | if (kerningRule == GLFontCharacterKerningRule.Zero) 37 | { 38 | return 0; 39 | } 40 | else if (kerningRule == GLFontCharacterKerningRule.NotMoreThanHalf) 41 | { 42 | return (int)Math.Min(Math.Min(g1.Rect.Width,g2.Rect.Width)*0.5f, worstCase); 43 | } 44 | 45 | return worstCase; 46 | } 47 | 48 | public static Dictionary CalculateKerning(char[] charSet, GLFontGlyph[] glyphs, List bitmapPages, GLFontKerningConfiguration config) 49 | { 50 | var kerningPairs = new Dictionary(); 51 | 52 | //we start by computing the index of the first and last non-empty pixel in each row of each glyph 53 | XLimits[][] limits = new XLimits[charSet.Length][]; 54 | int maxHeight = 0; 55 | for (int n = 0; n < charSet.Length; n++) 56 | { 57 | var rect = glyphs[n].Rect; 58 | var page = bitmapPages[glyphs[n].Page]; 59 | 60 | limits[n] = new XLimits[rect.Height]; 61 | 62 | maxHeight = Math.Max(rect.Height, maxHeight); 63 | 64 | int yStart = rect.Y; 65 | int yEnd = rect.Y + rect.Height; 66 | int xStart = rect.X; 67 | int xEnd = rect.X + rect.Width; 68 | 69 | for (int j = yStart; j < yEnd; j++) 70 | { 71 | int last = xStart; 72 | 73 | bool yetToFindFirst = true; 74 | for (int i = xStart; i < xEnd; i++) 75 | { 76 | if (!GLFontBitmap.EmptyAlphaPixel(page.bitmapData, i, j,config.AlphaEmptyPixelTolerance)) 77 | { 78 | 79 | if (yetToFindFirst) 80 | { 81 | limits[n][j - yStart].Min = i - xStart; 82 | yetToFindFirst = false; 83 | } 84 | last = i; 85 | } 86 | } 87 | 88 | limits[n][j - yStart].Max = last - xStart; 89 | 90 | if (yetToFindFirst) 91 | limits[n][j - yStart].Min = xEnd - 1; 92 | } 93 | } 94 | 95 | //we now bring up each row to the max (or min) of it's two adjacent rows, this is to stop glyphs sliding together too closely 96 | var tmp = new XLimits[maxHeight]; 97 | 98 | for (int n = 0; n < charSet.Length; n++) 99 | { 100 | //clear tmp 101 | for (int j = 0; j < limits[n].Length; j++) 102 | tmp[j] = limits[n][j]; 103 | 104 | for (int j = 0; j < limits[n].Length; j++) 105 | { 106 | if(j != 0){ 107 | tmp[j].Min = Math.Min(limits[n][j - 1].Min, tmp[j].Min); 108 | tmp[j].Max = Math.Max(limits[n][j - 1].Max, tmp[j].Max); 109 | } 110 | 111 | if (j != limits[n].Length - 1) 112 | { 113 | tmp[j].Min = Math.Min(limits[n][j + 1].Min, tmp[j].Min); 114 | tmp[j].Max = Math.Max(limits[n][j + 1].Max, tmp[j].Max); 115 | } 116 | 117 | } 118 | 119 | for (int j = 0; j < limits[n].Length; j++) 120 | limits[n][j] = tmp[j]; 121 | 122 | } 123 | 124 | for (int i = 0; i < charSet.Length; i++) 125 | for (int j = 0; j < charSet.Length; j++) 126 | kerningPairs.Add("" + charSet[i] + charSet[j], 1-Kerning(glyphs[i], glyphs[j], limits[i], limits[j],config)); 127 | 128 | return kerningPairs; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLScrollableControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | using OpenTK.Input; 5 | 6 | namespace GLGUI 7 | { 8 | public class GLScrollableControl : GLControl 9 | { 10 | public GLSlider Horizontal { get { return horizontal; } } 11 | public GLSlider Vertical { get { return vertical; } } 12 | public Point ScrollPosition { get { return scrollPosition; } } 13 | public Size ScrollFreedom { get { return scrollFreedom; } } 14 | public GLSkin.GLScrollableControlSkin Skin { get { return skin; } set { skin = value; Invalidate(); } } 15 | public override GLContextMenu ContextMenu { get { return base.ContextMenu; } set { base.ContextMenu = value; content.ContextMenu = value; } } 16 | 17 | private GLSkin.GLScrollableControlSkin skin; 18 | private Point scrollPosition; 19 | private Size scrollFreedom; 20 | private GLScrolledControl content; 21 | private GLSlider horizontal, vertical; 22 | 23 | public GLScrollableControl(GLGui gui) : base(gui) 24 | { 25 | Render += OnRender; 26 | MouseWheel += OnMouseWheel; 27 | 28 | skin = Gui.Skin.ScrollableControl; 29 | 30 | outer = new Rectangle(0, 0, 32, 32); 31 | horizontal = base.Add(new GLSlider(gui) { Direction = GLSliderOrientation.Horizontal/*, Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom*/ }); 32 | vertical = base.Add(new GLSlider(gui) { Direction = GLSliderOrientation.Vertical/*, Anchor = AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom*/ }); 33 | content = base.Add(new GLScrolledControl(gui) { /*Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom*/ }); 34 | content.MouseWheel += OnMouseWheel; 35 | 36 | horizontal.ValueChanged += (s, e) => Invalidate(); 37 | vertical.ValueChanged += (s, e) => Invalidate(); 38 | } 39 | 40 | protected override void UpdateLayout() 41 | { 42 | if (AutoSize) 43 | { 44 | if (Controls.Count() > 0) 45 | { 46 | outer.Width = Controls.Max(c => c.Outer.Right) + skin.Border.Horizontal; 47 | outer.Height = Controls.Max(c => c.Outer.Bottom) + skin.Border.Vertical; 48 | } 49 | else 50 | { 51 | outer.Width = skin.Border.Horizontal; 52 | outer.Height = skin.Border.Vertical; 53 | } 54 | } 55 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 56 | outer.Height = Math.Min(Math.Max(outer.Height, sizeMin.Height), sizeMax.Height); 57 | Inner = new Rectangle(skin.Border.Left, skin.Border.Top, outer.Width - skin.Border.Horizontal, outer.Height - skin.Border.Vertical); 58 | 59 | scrollFreedom = new Size(0, 0); 60 | if (content != null) 61 | { 62 | scrollFreedom = new Size( 63 | Math.Max(content.TotalSize.Width - (Inner.Width - vertical.Outer.Width), 0), 64 | Math.Max(content.TotalSize.Height - (Inner.Height - horizontal.Outer.Height), 0)); 65 | 66 | if ((scrollFreedom.Width == 0) == horizontal.Enabled) 67 | horizontal.Enabled = !horizontal.Enabled; 68 | if ((scrollFreedom.Height == 0) == vertical.Enabled) 69 | vertical.Enabled = !vertical.Enabled; 70 | 71 | if ((horizontal.Parent == null) && horizontal.Enabled) 72 | base.Add(horizontal); 73 | if ((horizontal.Parent != null) && !horizontal.Enabled) 74 | base.Remove(horizontal); 75 | if ((vertical.Parent == null) && vertical.Enabled) 76 | base.Add(vertical); 77 | if ((vertical.Parent != null) && !vertical.Enabled) 78 | base.Remove(vertical); 79 | 80 | scrollPosition = new Point((int)(horizontal.Value * scrollFreedom.Width), (int)(vertical.Value * scrollFreedom.Height)); 81 | horizontal.Outer = new Rectangle(0, Inner.Height - horizontal.Height, Inner.Width - (vertical.Enabled ? vertical.Width : 0), horizontal.Height); 82 | vertical.Outer = new Rectangle(Inner.Width - vertical.Width, 0, vertical.Width, Inner.Height - (horizontal.Enabled ? horizontal.Height : 0)); 83 | horizontal.MouseWheelStep = 1.0f / scrollFreedom.Width; 84 | vertical.MouseWheelStep = 1.0f / scrollFreedom.Height; 85 | 86 | content.Outer = new Rectangle(-scrollPosition.X, -scrollPosition.Y, 87 | Inner.Width - (vertical.Enabled ? vertical.Width : 0) + scrollPosition.X, 88 | Inner.Height - (horizontal.Enabled ? horizontal.Height : 0) + scrollPosition.Y); 89 | } 90 | } 91 | 92 | private void OnRender(object sender, double timeDelta) 93 | { 94 | GLDraw.Fill(ref skin.BorderColor); 95 | GLDraw.FillRect(Inner, ref skin.BackgroundColor); 96 | } 97 | 98 | public override T Add(T control) 99 | { 100 | return content.Add(control); 101 | } 102 | 103 | public override void Clear() 104 | { 105 | content.Clear(); 106 | } 107 | 108 | public override void Remove(GLControl control) 109 | { 110 | content.Remove(control); 111 | } 112 | 113 | private void OnMouseWheel(object sender, MouseWheelEventArgs e) 114 | { 115 | if (vertical.Enabled) 116 | vertical.DoMouseWheel(e); 117 | else 118 | horizontal.DoMouseWheel(e); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /GLGUI/bin/OpenTK.GLControl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OpenTK.GLControl 5 | 6 | 7 | 8 | 9 | OpenGL-aware WinForms control. 10 | The WinForms designer will always call the default constructor. 11 | Inherit from this class and call one of its specialized constructors 12 | to enable antialiasing or custom s. 13 | 14 | 15 | 16 | 17 | Required designer variable. 18 | 19 | 20 | 21 | 22 | Clean up any resources being used. 23 | 24 | true if managed resources should be disposed; otherwise, false. 25 | 26 | 27 | 28 | Required method for Designer support - do not modify 29 | the contents of this method with the code editor. 30 | 31 | 32 | 33 | 34 | Constructs a new instance. 35 | 36 | 37 | 38 | 39 | Constructs a new instance with the specified GraphicsMode. 40 | 41 | The OpenTK.Graphics.GraphicsMode of the control. 42 | 43 | 44 | 45 | Constructs a new instance with the specified GraphicsMode. 46 | 47 | The OpenTK.Graphics.GraphicsMode of the control. 48 | The major version for the OpenGL GraphicsContext. 49 | The minor version for the OpenGL GraphicsContext. 50 | The GraphicsContextFlags for the OpenGL GraphicsContext. 51 | 52 | 53 | Raises the HandleCreated event. 54 | Not used. 55 | 56 | 57 | Raises the HandleDestroyed event. 58 | Not used. 59 | 60 | 61 | 62 | Raises the System.Windows.Forms.Control.Paint event. 63 | 64 | A System.Windows.Forms.PaintEventArgs that contains the event data. 65 | 66 | 67 | 68 | Raises the Resize event. 69 | Note: this method may be called before the OpenGL context is ready. 70 | Check that IsHandleCreated is true before using any OpenGL methods. 71 | 72 | A System.EventArgs that contains the event data. 73 | 74 | 75 | 76 | Execute the delayed context update 77 | 78 | 79 | 80 | 81 | Raises the ParentChanged event. 82 | 83 | A System.EventArgs that contains the event data. 84 | 85 | 86 | 87 | Swaps the front and back buffers, presenting the rendered scene to the screen. 88 | This method will have no effect on a single-buffered GraphicsMode. 89 | 90 | 91 | 92 | 93 | 94 | Makes current in the calling thread. 95 | All OpenGL commands issued are hereafter interpreted by this context. 96 | 97 | 98 | When using multiple GLControls, calling MakeCurrent on 99 | one control will make all other controls non-current in the calling thread. 100 | 101 | 102 | 103 | A GLControl can only be current in one thread at a time. 104 | To make a control non-current, call GLControl.Context.MakeCurrent(null). 105 | 106 | 107 | 108 | 109 | 110 | Grabs a screenshot of the frontbuffer contents. 111 | When using multiple GLControls, ensure that 112 | is current before accessing this property. 113 | 114 | 115 | 116 | A System.Drawing.Bitmap, containing the contents of the frontbuffer. 117 | 118 | Occurs when no OpenTK.Graphics.GraphicsContext is current in the calling thread. 119 | 120 | 121 | 122 | 123 | Gets the CreateParams instance for this GLControl 124 | 125 | 126 | 127 | 128 | Gets a value indicating whether the current thread contains pending system messages. 129 | 130 | 131 | 132 | 133 | Gets the IGraphicsContext instance that is associated with the GLControl. 134 | The associated IGraphicsContext is updated whenever the GLControl 135 | handle is created or recreated. 136 | When using multiple GLControls, ensure that Context 137 | is current before performing any OpenGL operations. 138 | 139 | 140 | 141 | 142 | 143 | Gets the aspect ratio of this GLControl. 144 | 145 | 146 | 147 | 148 | Gets or sets a value indicating whether vsync is active for this GLControl. 149 | When using multiple GLControls, ensure that 150 | is current before accessing this property. 151 | 152 | 153 | 154 | 155 | 156 | 157 | Gets the GraphicsMode of the IGraphicsContext associated with 158 | this GLControl. If you wish to change GraphicsMode, you must 159 | destroy and recreate the GLControl. 160 | 161 | 162 | 163 | 164 | Gets the for this instance. 165 | 166 | 167 | 168 | 169 | Needed to delay the invoke on OS X. Also needed because OpenTK is .NET 2, otherwise I'd use an inline Action. 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.Example/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK; 4 | using OpenTK.Graphics; 5 | using OpenTK.Graphics.OpenGL; 6 | 7 | namespace GLGUI.Example 8 | { 9 | public class MainForm : GameWindow 10 | { 11 | GLGui glgui; 12 | GLLabel fpsLabel; 13 | GLLabel console; 14 | LineWriter consoleWriter; 15 | 16 | int fpsCounter = 0; 17 | int fpsSecond = 1; 18 | double time = 0.0; 19 | 20 | public MainForm() : base(1024, 600, new GraphicsMode(new ColorFormat(8, 8, 8, 8), 24, 0, 4), "GLGUI Example") 21 | { 22 | consoleWriter = new LineWriter(); 23 | Console.SetOut(consoleWriter); 24 | Console.SetError(consoleWriter); 25 | 26 | this.Load += OnLoad; 27 | this.Resize += (s, e) => GL.Viewport(ClientSize); 28 | this.RenderFrame += OnRender; 29 | } 30 | 31 | private void OnLoad(object sender, EventArgs e) 32 | { 33 | VSync = VSyncMode.Off; // vsync is nice, but you can't really measure performance while it's on 34 | 35 | glgui = new GLGui(this); 36 | 37 | var mainAreaControl = glgui.Add(new GLGroupLayout(glgui) { Size = new Size(ClientSize.Width, ClientSize.Height - 200), Anchor = GLAnchorStyles.All }); 38 | // change background color: 39 | var mainSkin = mainAreaControl.Skin; 40 | mainSkin.BackgroundColor = glgui.Skin.FormActive.BackgroundColor; 41 | mainSkin.BorderColor = glgui.Skin.FormActive.BorderColor; 42 | mainAreaControl.Skin = mainSkin; 43 | 44 | var consoleScrollControl = glgui.Add(new GLScrollableControl(glgui) { Outer = new Rectangle(0, ClientSize.Height - 200, ClientSize.Width, 200), Anchor = GLAnchorStyles.Left | GLAnchorStyles.Right | GLAnchorStyles.Bottom }); 45 | console = consoleScrollControl.Add(new GLLabel(glgui) { AutoSize = true, Multiline = true }); 46 | 47 | fpsLabel = mainAreaControl.Add(new GLLabel(glgui) { Location = new Point(10, 10), AutoSize = true }); 48 | // change font and background color: 49 | var skin = fpsLabel.SkinEnabled; 50 | skin.Font = new GLFont(new Font("Arial", 12.0f)); 51 | skin.BackgroundColor = glgui.Skin.TextBoxActive.BackgroundColor; 52 | fpsLabel.SkinEnabled = skin; 53 | 54 | var helloWorldForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Hello World", Outer = new Rectangle(50, 100, 200, 150), AutoSize = false }); 55 | helloWorldForm.Add(new GLForm(glgui) { Title = "Hello Form", Outer = new Rectangle(100, 32, 100, 100), AutoSize = false }) 56 | .MouseMove += (s, w) => Console.WriteLine(w.Position.ToString()); 57 | 58 | var flow = helloWorldForm.Add(new GLFlowLayout(glgui) { FlowDirection = GLFlowDirection.BottomUp, Location = new Point(10, 10), Size = helloWorldForm.InnerSize, AutoSize = true }); 59 | for (int i = 0; i < 5; i++) 60 | flow.Add(new GLButton(glgui) { Text = "Button" + i, Size = new Size(150, 0) }) 61 | .Click += (s, w) => Console.WriteLine(s + " pressed."); 62 | flow.Add(new GLButton(glgui) { Text = "Hide Cursor", Size = new Size(150, 0) }) 63 | .Click += (s, w) => glgui.Cursor = GLCursor.None; 64 | 65 | var loremIpsumForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Lorem Ipsum", Location = new Point(600, 100), Size = new Size(300, 200) }); 66 | loremIpsumForm.Add(new GLTextBox(glgui) { 67 | Text = "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\nsed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,\nsed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", 68 | Multiline = true, 69 | WordWrap = true, 70 | Outer = new Rectangle(4, 4, loremIpsumForm.Inner.Width - 8, loremIpsumForm.Inner.Height - 8), 71 | Anchor = GLAnchorStyles.All 72 | }).Changed += (s, w) => Console.WriteLine(s + " text length: " + ((GLTextBox)s).Text.Length); 73 | 74 | var fixedSizeForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Fixed size Form", Location = new Point(64, 300), Size = new Size(100, 200), AutoSize = true }); 75 | var fooBarFlow = fixedSizeForm.Add(new GLFlowLayout(glgui) { FlowDirection = GLFlowDirection.TopDown, AutoSize = true }); 76 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "CheckBox A", AutoSize = true }) 77 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 78 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "CheckBox B", AutoSize = true }) 79 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 80 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "Totally different CheckBox", AutoSize = true }) 81 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 82 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "Go away!", AutoSize = true }) 83 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 84 | 85 | for (int i = 0; i < 3; i++) 86 | { 87 | var viewportForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Cube", Location = new Point(300 + i * 16, 64 + i * 16), Size = new Size(200, 200) }); 88 | viewportForm.Add(new GLViewport(glgui) { Size = viewportForm.InnerSize, Anchor = GLAnchorStyles.All }) 89 | .RenderViewport += OnRenderViewport; 90 | } 91 | } 92 | 93 | private void OnRender(object sender, FrameEventArgs e) 94 | { 95 | time += e.Time; 96 | 97 | if (time >= fpsSecond) 98 | { 99 | fpsLabel.Text = string.Format("Application: {0:0}FPS. GLGUI: {1:0.0}ms", fpsCounter, glgui.RenderDuration); 100 | fpsCounter = 0; 101 | fpsSecond++; 102 | } 103 | 104 | if (consoleWriter.Changed) 105 | { 106 | console.Text = string.Join("\n", consoleWriter.Lines); 107 | consoleWriter.Changed = false; 108 | } 109 | 110 | glgui.Render(); 111 | SwapBuffers(); 112 | 113 | fpsCounter++; 114 | } 115 | 116 | // draws a simple colored cube in a GLViewport control 117 | private void OnRenderViewport(object sender, double deltaTime) 118 | { 119 | var viewport = (GLViewport)sender; 120 | 121 | GL.Enable(EnableCap.DepthTest); 122 | GL.ClearColor(0, 0, 0, 1); 123 | GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); 124 | 125 | GL.MatrixMode(MatrixMode.Projection); 126 | var proj = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(90.0f), viewport.AspectRatio, 1.0f, 100.0f); 127 | GL.LoadMatrix(ref proj); 128 | GL.MatrixMode(MatrixMode.Modelview); 129 | GL.LoadIdentity(); 130 | 131 | GL.Translate(0, 0, -2.0f); 132 | GL.Rotate(time * 100.0f, 1, 0, 0); 133 | GL.Rotate(time * 42.0f, 0, 1, 0); 134 | 135 | GL.Begin(PrimitiveType.Quads); 136 | GL.Color3(1.0, 0.0, 0.0); 137 | GL.Vertex3(0.5, -0.5, -0.5); 138 | GL.Color3(0.0, 1.0, 0.0); 139 | GL.Vertex3(0.5, 0.5, -0.5); 140 | GL.Color3(0.0, 0.0, 1.0); 141 | GL.Vertex3(-0.5, 0.5, -0.5); 142 | GL.Color3(1.0, 0.0, 1.0); 143 | GL.Vertex3(-0.5, -0.5, -0.5); 144 | 145 | GL.Color3(1.0, 1.0, 1.0); 146 | GL.Vertex3(0.5, -0.5, 0.5); 147 | GL.Vertex3(0.5, 0.5, 0.5); 148 | GL.Vertex3(-0.5, 0.5, 0.5); 149 | GL.Vertex3(-0.5, -0.5, 0.5); 150 | 151 | GL.Color3(1.0, 0.0, 1.0); 152 | GL.Vertex3(0.5, -0.5, -0.5); 153 | GL.Vertex3(0.5, 0.5, -0.5); 154 | GL.Vertex3(0.5, 0.5, 0.5); 155 | GL.Vertex3(0.5, -0.5, 0.5); 156 | 157 | GL.Color3(0.0, 1.0, 0.0); 158 | GL.Vertex3(-0.5, -0.5, 0.5); 159 | GL.Vertex3(-0.5, 0.5, 0.5); 160 | GL.Vertex3(-0.5, 0.5, -0.5); 161 | GL.Vertex3(-0.5, -0.5, -0.5); 162 | 163 | GL.Color3(0.0, 0.0, 1.0); 164 | GL.Vertex3(0.5, 0.5, 0.5); 165 | GL.Vertex3(0.5, 0.5, -0.5); 166 | GL.Vertex3(-0.5, 0.5, -0.5); 167 | GL.Vertex3(-0.5, 0.5, 0.5); 168 | 169 | GL.Color3(1.0, 0.0, 0.0); 170 | GL.Vertex3(0.5, -0.5, -0.5); 171 | GL.Vertex3(0.5, -0.5, 0.5); 172 | GL.Vertex3(-0.5, -0.5, 0.5); 173 | GL.Vertex3(-0.5, -0.5, -0.5); 174 | GL.End(); 175 | 176 | GL.Disable(EnableCap.DepthTest); 177 | } 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFontTextNodeList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Text; 4 | 5 | namespace GLGUI 6 | { 7 | /// 8 | /// A doubly linked list of text nodes 9 | /// 10 | class GLFontTextNodeList : IEnumerable 11 | { 12 | public GLFontTextNode Head; 13 | public GLFontTextNode Tail; 14 | 15 | /// 16 | /// Builds a doubly linked list of text nodes from the given input string 17 | /// 18 | /// 19 | public GLFontTextNodeList(string text) 20 | { 21 | bool carriageReturn = false; 22 | bool wordInProgress = false; 23 | StringBuilder currentWord = new StringBuilder(); 24 | 25 | for (int i = 0; i < text.Length; i++) 26 | { 27 | if (text[i] == '\r' || text[i] == '\n' || text[i] == ' ' || text[i] == '\t') 28 | { 29 | if (text[i] == '\n' && carriageReturn) //text = text.Replace("\r\n", "\r"); 30 | continue; 31 | carriageReturn = false; 32 | if (text[i] == '\r') 33 | carriageReturn = true; 34 | 35 | if (wordInProgress) 36 | { 37 | Add(new GLFontTextNode(GLFontTextNodeType.Word, currentWord.ToString())); 38 | wordInProgress = false; 39 | } 40 | 41 | if (text[i] == '\r' || text[i] == '\n') 42 | Add(new GLFontTextNode(GLFontTextNodeType.LineBreak, null)); 43 | else if (text[i] == ' ') 44 | Add(new GLFontTextNode(GLFontTextNodeType.Space, null)); 45 | else if (text[i] == '\t') 46 | Add(new GLFontTextNode(GLFontTextNodeType.Tab, null)); 47 | } 48 | else 49 | { 50 | carriageReturn = false; 51 | if (!wordInProgress) 52 | { 53 | wordInProgress = true; 54 | currentWord = new StringBuilder(); 55 | } 56 | currentWord.Append(text[i]); 57 | } 58 | } 59 | 60 | if (wordInProgress) 61 | Add(new GLFontTextNode(GLFontTextNodeType.Word, currentWord.ToString())); 62 | } 63 | 64 | public void MeasureNodes(GLFontData fontData, GLFontRenderOptions options) 65 | { 66 | bool monospaced = fontData.IsMonospacingActive(options); 67 | float monospaceWidth = fontData.GetMonoSpaceWidth(options); 68 | foreach (GLFontTextNode node in this) 69 | { 70 | if (node.Length == 0f) 71 | { 72 | if (node.Type == GLFontTextNodeType.Space) 73 | { 74 | if (monospaced) 75 | { 76 | node.Length = monospaceWidth; 77 | continue; 78 | } 79 | node.Length = (float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing); 80 | continue; 81 | } 82 | 83 | if (node.Type == GLFontTextNodeType.Tab) 84 | { 85 | if (monospaced) 86 | { 87 | node.Length = monospaceWidth * 4; 88 | continue; 89 | } 90 | node.Length = (float)Math.Ceiling(4 * fontData.meanGlyphWidth * options.WordSpacing); 91 | continue; 92 | } 93 | 94 | if (node.Type == GLFontTextNodeType.Word) 95 | { 96 | for (int i = 0; i < node.Text.Length; i++) 97 | { 98 | char c = node.Text[i]; 99 | GLFontGlyph glyph; 100 | if (fontData.CharSetMapping.TryGetValue(c, out glyph)) 101 | { 102 | if (monospaced) 103 | node.Length += monospaceWidth; 104 | else 105 | node.Length += (float)Math.Ceiling(glyph.Rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node)); 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | 113 | /// 114 | /// Splits a word into sub-words of size less than or equal to baseCaseSize 115 | /// 116 | /// 117 | /// 118 | public void Crumble(GLFontTextNode node, int baseCaseSize) 119 | { 120 | if(node.Text.Length <= baseCaseSize) 121 | return; 122 | 123 | var left = SplitNode(node); 124 | var right = left.Next; 125 | Crumble(left, baseCaseSize); 126 | Crumble(right, baseCaseSize); 127 | } 128 | 129 | /// 130 | /// Splits a word node in two, adding both new nodes to the list in sequence. 131 | /// 132 | /// 133 | /// The first new node 134 | public GLFontTextNode SplitNode(GLFontTextNode node) 135 | { 136 | if (node.Type != GLFontTextNodeType.Word) 137 | throw new Exception("Cannot split text node of type: " + node.Type); 138 | 139 | int midPoint = node.Text.Length / 2; 140 | 141 | string newFirstHalf = node.Text.Substring(0, midPoint); 142 | string newSecondHalf = node.Text.Substring(midPoint, node.Text.Length - midPoint); 143 | 144 | GLFontTextNode newFirst = new GLFontTextNode(GLFontTextNodeType.Word, newFirstHalf); 145 | GLFontTextNode newSecond = new GLFontTextNode(GLFontTextNodeType.Word, newSecondHalf); 146 | newFirst.Next = newSecond; 147 | newSecond.Previous = newFirst; 148 | 149 | //node is head 150 | if (node.Previous == null) 151 | Head = newFirst; 152 | else 153 | { 154 | node.Previous.Next = newFirst; 155 | newFirst.Previous = node.Previous; 156 | } 157 | 158 | //node is tail 159 | if (node.Next == null) 160 | Tail = newSecond; 161 | else 162 | { 163 | node.Next.Previous = newSecond; 164 | newSecond.Next = node.Next; 165 | } 166 | 167 | return newFirst; 168 | } 169 | 170 | public void Add(GLFontTextNode node) 171 | { 172 | if(Head == null) 173 | { 174 | Head = Tail = node; 175 | } 176 | else 177 | { 178 | Tail.Next = node; 179 | node.Previous = Tail; 180 | Tail = node; 181 | } 182 | } 183 | 184 | public override string ToString() 185 | { 186 | StringBuilder builder = new StringBuilder(); 187 | 188 | foreach(GLFontTextNode node in this) 189 | { 190 | if (node.Type == GLFontTextNodeType.Space) 191 | builder.Append(" "); 192 | else if (node.Type == GLFontTextNodeType.Tab) 193 | builder.Append(" "); 194 | else if (node.Type == GLFontTextNodeType.LineBreak) 195 | builder.Append(System.Environment.NewLine); 196 | else if (node.Type == GLFontTextNodeType.Word) 197 | builder.Append("" + node.Text + ""); 198 | } 199 | 200 | return builder.ToString(); 201 | } 202 | 203 | public IEnumerator GetEnumerator() 204 | { 205 | return new TextNodeListEnumerator(this); 206 | } 207 | 208 | private class TextNodeListEnumerator : IEnumerator 209 | { 210 | private GLFontTextNode currentNode = null; 211 | private GLFontTextNodeList targetList; 212 | 213 | public TextNodeListEnumerator(GLFontTextNodeList targetList) 214 | { 215 | this.targetList = targetList; 216 | } 217 | 218 | public object Current 219 | { 220 | get { return currentNode; } 221 | } 222 | 223 | public virtual bool MoveNext() 224 | { 225 | if (currentNode == null) 226 | currentNode = targetList.Head; 227 | else 228 | currentNode = currentNode.Next; 229 | return currentNode != null; 230 | } 231 | 232 | public void Reset() 233 | { 234 | currentNode = null; 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontTextNodeList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Text; 4 | 5 | namespace GLGUI 6 | { 7 | /// 8 | /// A doubly linked list of text nodes 9 | /// 10 | class GLFontTextNodeList : IEnumerable 11 | { 12 | public GLFontTextNode Head; 13 | public GLFontTextNode Tail; 14 | 15 | /// 16 | /// Builds a doubly linked list of text nodes from the given input string 17 | /// 18 | /// 19 | public GLFontTextNodeList(string text) 20 | { 21 | bool carriageReturn = false; 22 | bool wordInProgress = false; 23 | StringBuilder currentWord = new StringBuilder(); 24 | 25 | for (int i = 0; i < text.Length; i++) 26 | { 27 | if (text[i] == '\r' || text[i] == '\n' || text[i] == ' ' || text[i] == '\t') 28 | { 29 | if (text[i] == '\n' && carriageReturn) //text = text.Replace("\r\n", "\r"); 30 | continue; 31 | carriageReturn = false; 32 | if (text[i] == '\r') 33 | carriageReturn = true; 34 | 35 | if (wordInProgress) 36 | { 37 | Add(new GLFontTextNode(GLFontTextNodeType.Word, currentWord.ToString())); 38 | wordInProgress = false; 39 | } 40 | 41 | if (text[i] == '\r' || text[i] == '\n') 42 | Add(new GLFontTextNode(GLFontTextNodeType.LineBreak, null)); 43 | else if (text[i] == ' ') 44 | Add(new GLFontTextNode(GLFontTextNodeType.Space, null)); 45 | else if (text[i] == '\t') 46 | Add(new GLFontTextNode(GLFontTextNodeType.Tab, null)); 47 | } 48 | else 49 | { 50 | carriageReturn = false; 51 | if (!wordInProgress) 52 | { 53 | wordInProgress = true; 54 | currentWord = new StringBuilder(); 55 | } 56 | currentWord.Append(text[i]); 57 | } 58 | } 59 | 60 | if (wordInProgress) 61 | Add(new GLFontTextNode(GLFontTextNodeType.Word, currentWord.ToString())); 62 | } 63 | 64 | public void MeasureNodes(GLFontData fontData, GLFontRenderOptions options) 65 | { 66 | bool monospaced = fontData.IsMonospacingActive(options); 67 | float monospaceWidth = fontData.GetMonoSpaceWidth(options); 68 | foreach (GLFontTextNode node in this) 69 | { 70 | if (node.Length == 0f) 71 | { 72 | if (node.Type == GLFontTextNodeType.Space) 73 | { 74 | if (monospaced) 75 | { 76 | node.Length = monospaceWidth; 77 | continue; 78 | } 79 | node.Length = (float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing); 80 | continue; 81 | } 82 | 83 | if (node.Type == GLFontTextNodeType.Tab) 84 | { 85 | if (monospaced) 86 | { 87 | node.Length = monospaceWidth * 4; 88 | continue; 89 | } 90 | node.Length = (float)Math.Ceiling(4 * fontData.meanGlyphWidth * options.WordSpacing); 91 | continue; 92 | } 93 | 94 | if (node.Type == GLFontTextNodeType.Word) 95 | { 96 | for (int i = 0; i < node.Text.Length; i++) 97 | { 98 | char c = node.Text[i]; 99 | GLFontGlyph glyph; 100 | if (fontData.CharSetMapping.TryGetValue(c, out glyph)) 101 | { 102 | if (monospaced) 103 | node.Length += monospaceWidth; 104 | else 105 | node.Length += (float)Math.Ceiling(glyph.Rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node)); 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | 113 | /// 114 | /// Splits a word into sub-words of size less than or equal to baseCaseSize 115 | /// 116 | /// 117 | /// 118 | public void Crumble(GLFontTextNode node, int baseCaseSize) 119 | { 120 | if(node.Text.Length <= baseCaseSize) 121 | return; 122 | 123 | var left = SplitNode(node); 124 | var right = left.Next; 125 | Crumble(left, baseCaseSize); 126 | Crumble(right, baseCaseSize); 127 | } 128 | 129 | /// 130 | /// Splits a word node in two, adding both new nodes to the list in sequence. 131 | /// 132 | /// 133 | /// The first new node 134 | public GLFontTextNode SplitNode(GLFontTextNode node) 135 | { 136 | if (node.Type != GLFontTextNodeType.Word) 137 | throw new Exception("Cannot split text node of type: " + node.Type); 138 | 139 | int midPoint = node.Text.Length / 2; 140 | 141 | string newFirstHalf = node.Text.Substring(0, midPoint); 142 | string newSecondHalf = node.Text.Substring(midPoint, node.Text.Length - midPoint); 143 | 144 | GLFontTextNode newFirst = new GLFontTextNode(GLFontTextNodeType.Word, newFirstHalf); 145 | GLFontTextNode newSecond = new GLFontTextNode(GLFontTextNodeType.Word, newSecondHalf); 146 | newFirst.Next = newSecond; 147 | newSecond.Previous = newFirst; 148 | 149 | //node is head 150 | if (node.Previous == null) 151 | Head = newFirst; 152 | else 153 | { 154 | node.Previous.Next = newFirst; 155 | newFirst.Previous = node.Previous; 156 | } 157 | 158 | //node is tail 159 | if (node.Next == null) 160 | Tail = newSecond; 161 | else 162 | { 163 | node.Next.Previous = newSecond; 164 | newSecond.Next = node.Next; 165 | } 166 | 167 | return newFirst; 168 | } 169 | 170 | public void Add(GLFontTextNode node) 171 | { 172 | if(Head == null) 173 | { 174 | Head = Tail = node; 175 | } 176 | else 177 | { 178 | Tail.Next = node; 179 | node.Previous = Tail; 180 | Tail = node; 181 | } 182 | } 183 | 184 | public override string ToString() 185 | { 186 | StringBuilder builder = new StringBuilder(); 187 | 188 | foreach(GLFontTextNode node in this) 189 | { 190 | if (node.Type == GLFontTextNodeType.Space) 191 | builder.Append(" "); 192 | else if (node.Type == GLFontTextNodeType.Tab) 193 | builder.Append(" "); 194 | else if (node.Type == GLFontTextNodeType.LineBreak) 195 | builder.Append(System.Environment.NewLine); 196 | else if (node.Type == GLFontTextNodeType.Word) 197 | builder.Append("" + node.Text + ""); 198 | } 199 | 200 | return builder.ToString(); 201 | } 202 | 203 | public IEnumerator GetEnumerator() 204 | { 205 | return new TextNodeListEnumerator(this); 206 | } 207 | 208 | private class TextNodeListEnumerator : IEnumerator 209 | { 210 | private GLFontTextNode currentNode = null; 211 | private GLFontTextNodeList targetList; 212 | 213 | public TextNodeListEnumerator(GLFontTextNodeList targetList) 214 | { 215 | this.targetList = targetList; 216 | } 217 | 218 | public object Current 219 | { 220 | get { return currentNode; } 221 | } 222 | 223 | public virtual bool MoveNext() 224 | { 225 | if (currentNode == null) 226 | currentNode = targetList.Head; 227 | else 228 | currentNode = currentNode.Next; 229 | return currentNode != null; 230 | } 231 | 232 | public void Reset() 233 | { 234 | currentNode = null; 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /GLGUI/GLGUI.GLControlExample/GuiControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using OpenTK; 4 | using OpenTK.Graphics; 5 | using OpenTK.Graphics.OpenGL; 6 | using System.Windows.Forms; 7 | using System.Diagnostics; 8 | 9 | namespace GLGUI.GLControlExample 10 | { 11 | public class GuiControl : OpenTK.GLControl 12 | { 13 | GLGui glgui; 14 | GLLabel fpsLabel; 15 | GLLabel console; 16 | LineWriter consoleWriter; 17 | 18 | Stopwatch stopwatch; 19 | int fpsCounter = 0; 20 | int fpsSecond = 1; 21 | double time = 0.0; 22 | 23 | public GuiControl() : base(new GraphicsMode(new ColorFormat(8, 8, 8, 8), 24, 0, 4)) 24 | { 25 | consoleWriter = new LineWriter(); 26 | Console.SetOut(consoleWriter); 27 | Console.SetError(consoleWriter); 28 | 29 | this.Load += OnLoad; 30 | } 31 | 32 | private void OnLoad(object sender, EventArgs e) 33 | { 34 | VSync = false; // vsync is nice, but you can't really measure performance while it's on 35 | 36 | glgui = new GLGui(this); 37 | 38 | var mainAreaControl = glgui.Add(new GLGroupLayout(glgui) { Size = new Size(ClientSize.Width, ClientSize.Height - 200), Anchor = GLAnchorStyles.All }); 39 | // change background color: 40 | var mainSkin = mainAreaControl.Skin; 41 | mainSkin.BackgroundColor = glgui.Skin.FormActive.BackgroundColor; 42 | mainSkin.BorderColor = glgui.Skin.FormActive.BorderColor; 43 | mainAreaControl.Skin = mainSkin; 44 | 45 | var consoleScrollControl = glgui.Add(new GLScrollableControl(glgui) { Outer = new Rectangle(0, ClientSize.Height - 200, ClientSize.Width, 200), Anchor = GLAnchorStyles.Left | GLAnchorStyles.Right | GLAnchorStyles.Bottom }); 46 | console = consoleScrollControl.Add(new GLLabel(glgui) { AutoSize = true, Multiline = true }); 47 | 48 | fpsLabel = mainAreaControl.Add(new GLLabel(glgui) { Location = new Point(10, 10), AutoSize = true }); 49 | // change font and background color: 50 | var skin = fpsLabel.SkinEnabled; 51 | skin.Font = new GLFont(new Font("Arial", 12.0f)); 52 | skin.BackgroundColor = glgui.Skin.TextBoxActive.BackgroundColor; 53 | fpsLabel.SkinEnabled = skin; 54 | 55 | var helloWorldForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Hello World", Outer = new Rectangle(50, 100, 200, 150), AutoSize = false }); 56 | helloWorldForm.Add(new GLForm(glgui) { Title = "Hello Form", Outer = new Rectangle(100, 32, 100, 100), AutoSize = false }) 57 | .MouseMove += (s, w) => Console.WriteLine(w.Position.ToString()); 58 | 59 | var flow = helloWorldForm.Add(new GLFlowLayout(glgui) { FlowDirection = GLFlowDirection.BottomUp, Location = new Point(10, 10), Size = helloWorldForm.InnerSize, AutoSize = true }); 60 | for (int i = 0; i < 5; i++) 61 | flow.Add(new GLButton(glgui) { Text = "Button" + i, Size = new Size(150, 0) }) 62 | .Click += (s, w) => Console.WriteLine(s + " pressed."); 63 | flow.Add(new GLButton(glgui) { Text = "Hide Cursor", Size = new Size(150, 0) }) 64 | .Click += (s, w) => glgui.Cursor = GLCursor.None; 65 | 66 | var loremIpsumForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Lorem Ipsum", Location = new Point(600, 100), Size = new Size(300, 200) }); 67 | loremIpsumForm.Add(new GLTextBox(glgui) { 68 | Text = "Lorem ipsum dolor sit amet,\nconsetetur sadipscing elitr,\nsed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,\nsed diam voluptua.\n\nAt vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.", 69 | Multiline = true, 70 | WordWrap = true, 71 | Outer = new Rectangle(4, 4, loremIpsumForm.Inner.Width - 8, loremIpsumForm.Inner.Height - 8), 72 | Anchor = GLAnchorStyles.All 73 | }).Changed += (s, w) => Console.WriteLine(s + " text length: " + ((GLTextBox)s).Text.Length); 74 | 75 | var fixedSizeForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Fixed size Form", Location = new Point(64, 300), Size = new Size(100, 200), AutoSize = true }); 76 | var fooBarFlow = fixedSizeForm.Add(new GLFlowLayout(glgui) { FlowDirection = GLFlowDirection.TopDown, AutoSize = true }); 77 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "CheckBox A", AutoSize = true }) 78 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 79 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "CheckBox B", AutoSize = true }) 80 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 81 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "Totally different CheckBox", AutoSize = true }) 82 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 83 | fooBarFlow.Add(new GLCheckBox(glgui) { Text = "Go away!", AutoSize = true }) 84 | .Changed += (s, w) => Console.WriteLine(s + ": " + ((GLCheckBox)s).Checked); 85 | 86 | for (int i = 0; i < 3; i++) 87 | { 88 | var viewportForm = mainAreaControl.Add(new GLForm(glgui) { Title = "Cube", Location = new Point(300 + i * 16, 64 + i * 16), Size = new Size(200, 200) }); 89 | viewportForm.Add(new GLViewport(glgui) { Size = viewportForm.InnerSize, Anchor = GLAnchorStyles.All }) 90 | .RenderViewport += OnRenderViewport; 91 | } 92 | 93 | stopwatch = new Stopwatch(); 94 | Resize += (s, ev) => GL.Viewport(ClientSize); 95 | Paint += OnRender; 96 | 97 | stopwatch.Start(); 98 | Application.Idle += (s, ev) => Invalidate(); 99 | } 100 | 101 | private void OnRender(object sender, PaintEventArgs e) 102 | { 103 | stopwatch.Stop(); 104 | double delta = stopwatch.Elapsed.TotalMilliseconds * 0.001; 105 | stopwatch.Restart(); 106 | time += delta; 107 | 108 | if (time >= fpsSecond) 109 | { 110 | fpsLabel.Text = string.Format("Application: {0:0}FPS. GLGUI: {1:0.0}ms", fpsCounter, glgui.RenderDuration); 111 | fpsCounter = 0; 112 | fpsSecond++; 113 | } 114 | 115 | if (consoleWriter.Changed) 116 | { 117 | console.Text = string.Join("\n", consoleWriter.Lines); 118 | consoleWriter.Changed = false; 119 | } 120 | 121 | glgui.Render(); 122 | SwapBuffers(); 123 | 124 | fpsCounter++; 125 | } 126 | 127 | // draws a simple colored cube in a GLViewport control 128 | private void OnRenderViewport(object sender, double deltaTime) 129 | { 130 | var viewport = (GLViewport)sender; 131 | 132 | GL.Enable(EnableCap.DepthTest); 133 | GL.ClearColor(0, 0, 0, 1); 134 | GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); 135 | 136 | GL.MatrixMode(MatrixMode.Projection); 137 | var proj = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(90.0f), viewport.AspectRatio, 1.0f, 100.0f); 138 | GL.LoadMatrix(ref proj); 139 | GL.MatrixMode(MatrixMode.Modelview); 140 | GL.LoadIdentity(); 141 | 142 | GL.Translate(0, 0, -2.0f); 143 | GL.Rotate(time * 100.0f, 1, 0, 0); 144 | GL.Rotate(time * 42.0f, 0, 1, 0); 145 | 146 | GL.Begin(PrimitiveType.Quads); 147 | GL.Color3(1.0, 0.0, 0.0); 148 | GL.Vertex3(0.5, -0.5, -0.5); 149 | GL.Color3(0.0, 1.0, 0.0); 150 | GL.Vertex3(0.5, 0.5, -0.5); 151 | GL.Color3(0.0, 0.0, 1.0); 152 | GL.Vertex3(-0.5, 0.5, -0.5); 153 | GL.Color3(1.0, 0.0, 1.0); 154 | GL.Vertex3(-0.5, -0.5, -0.5); 155 | 156 | GL.Color3(1.0, 1.0, 1.0); 157 | GL.Vertex3(0.5, -0.5, 0.5); 158 | GL.Vertex3(0.5, 0.5, 0.5); 159 | GL.Vertex3(-0.5, 0.5, 0.5); 160 | GL.Vertex3(-0.5, -0.5, 0.5); 161 | 162 | GL.Color3(1.0, 0.0, 1.0); 163 | GL.Vertex3(0.5, -0.5, -0.5); 164 | GL.Vertex3(0.5, 0.5, -0.5); 165 | GL.Vertex3(0.5, 0.5, 0.5); 166 | GL.Vertex3(0.5, -0.5, 0.5); 167 | 168 | GL.Color3(0.0, 1.0, 0.0); 169 | GL.Vertex3(-0.5, -0.5, 0.5); 170 | GL.Vertex3(-0.5, 0.5, 0.5); 171 | GL.Vertex3(-0.5, 0.5, -0.5); 172 | GL.Vertex3(-0.5, -0.5, -0.5); 173 | 174 | GL.Color3(0.0, 0.0, 1.0); 175 | GL.Vertex3(0.5, 0.5, 0.5); 176 | GL.Vertex3(0.5, 0.5, -0.5); 177 | GL.Vertex3(-0.5, 0.5, -0.5); 178 | GL.Vertex3(-0.5, 0.5, 0.5); 179 | 180 | GL.Color3(1.0, 0.0, 0.0); 181 | GL.Vertex3(0.5, -0.5, -0.5); 182 | GL.Vertex3(0.5, -0.5, 0.5); 183 | GL.Vertex3(-0.5, -0.5, 0.5); 184 | GL.Vertex3(-0.5, -0.5, -0.5); 185 | GL.End(); 186 | 187 | GL.Disable(EnableCap.DepthTest); 188 | } 189 | 190 | protected override bool IsInputKey(Keys key) 191 | { 192 | return true; 193 | } 194 | } 195 | } 196 | 197 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Linq; 4 | using OpenTK.Input; 5 | 6 | namespace GLGUI 7 | { 8 | public class GLForm : GLControl 9 | { 10 | public string Title { get { return title; } set { if (value != title) { title = value; Invalidate(); } } } 11 | public bool Maximized { get { return maximized; } set { Maximize(value); } } 12 | public GLSkin.GLFormSkin SkinActive { get { return skinActive; } set { skinActive = value; Invalidate(); } } 13 | public GLSkin.GLFormSkin SkinInactive { get { return skinInactive; } set { skinInactive = value; Invalidate(); } } 14 | 15 | private string title = ""; 16 | private GLFontText titleProcessed = new GLFontText(); 17 | private GLSkin.GLFormSkin skinActive, skinInactive; 18 | private GLSkin.GLFormSkin skin; 19 | private enum DragOperation { Move = 0, ResizeNW, ResizeN, ResizeNE, ResizeE, ResizeSE, ResizeS, ResizeSW, ResizeW } 20 | private DragOperation dragOp; 21 | private Rectangle moveClickRegion; 22 | private Point mouseOffset; 23 | private SizeF titleSize; 24 | private int minHeight; 25 | private DateTime lastClick; 26 | 27 | private static GLCursor[] dragOpCursors = new GLCursor[] 28 | { 29 | GLCursor.SizeAll, 30 | GLCursor.SizeNWSE, GLCursor.SizeNS, GLCursor.SizeNESW, GLCursor.SizeWE, 31 | GLCursor.SizeNWSE, GLCursor.SizeNS, GLCursor.SizeNESW, GLCursor.SizeWE 32 | }; 33 | 34 | public GLForm(GLGui gui) : base(gui) 35 | { 36 | Render += OnRender; 37 | MouseDown += OnMouseDown; 38 | MouseUp += OnMouseUp; 39 | MouseMove += OnMouseMove; 40 | MouseLeave += OnMouseLeave; 41 | //MouseDoubleClick += OnMouseDoubleClick; 42 | Focus += (s, e) => Invalidate(); 43 | FocusLost += (s, e) => Invalidate(); 44 | 45 | skinActive = Gui.Skin.FormActive; 46 | skinInactive = Gui.Skin.FormInactive; 47 | 48 | outer = new Rectangle(0, 0, 100, 100); 49 | sizeMin = new Size(64, 32); 50 | sizeMax = new Size(int.MaxValue, int.MaxValue); 51 | } 52 | 53 | protected override void UpdateLayout() 54 | { 55 | skin = HasFocus ? skinActive : skinInactive; 56 | 57 | if (AutoSize) 58 | { 59 | if (Controls.Count() > 0) 60 | { 61 | outer.Width = Controls.Max(c => c.Outer.Right) + skin.Border.Horizontal; 62 | outer.Height = Controls.Max(c => c.Outer.Bottom) + skin.Border.Vertical + (int)titleSize.Height + skin.Border.Top; 63 | } 64 | else 65 | { 66 | outer.Width = 0; 67 | outer.Height = 0; 68 | } 69 | } 70 | 71 | outer.Width = Math.Min(Math.Max(outer.Width, sizeMin.Width), sizeMax.Width); 72 | 73 | int innerWidth = outer.Width - skin.Border.Horizontal; 74 | titleSize = skin.Font.ProcessText(titleProcessed, title, GLFontAlignment.Left); 75 | minHeight = Math.Max(sizeMin.Height, (int)titleSize.Height + skin.Border.Vertical + skin.Border.Top); 76 | 77 | outer.Height = Math.Min(Math.Max(outer.Height, minHeight), sizeMax.Height); 78 | if (Parent != null) 79 | { 80 | outer.X = Math.Max(Math.Min(outer.X, Parent.Inner.Width - outer.Width), 0); 81 | outer.Y = Math.Max(Math.Min(outer.Y, Parent.Inner.Height - outer.Height), 0); 82 | outer.Width = Math.Min(outer.Right, Parent.Inner.Width) - outer.X; 83 | outer.Height = Math.Min(outer.Bottom, Parent.Inner.Height) - outer.Y; 84 | } 85 | 86 | Inner = new Rectangle( 87 | skin.Border.Left, skin.Border.Top + (int)titleSize.Height + skin.Border.Top, 88 | innerWidth, outer.Height - skin.Border.Vertical - (int)titleSize.Height - skin.Border.Top); 89 | moveClickRegion = new Rectangle(skin.Border.Left, skin.Border.Top, Inner.Width, (int)titleSize.Height); 90 | } 91 | 92 | private void OnRender(object sender, double timeDelta) 93 | { 94 | GLDraw.Fill(ref skin.BorderColor); 95 | GLDraw.FillRect(Inner, ref skin.BackgroundColor); 96 | GLDraw.Text(titleProcessed, ref moveClickRegion, ref skin.Color); 97 | } 98 | 99 | private void StartDragOperation(DragOperation op, Point p) 100 | { 101 | if ((AutoSize || maximized) && op != DragOperation.Move) 102 | return; 103 | 104 | mouseOffset = p; 105 | isDragged = true; 106 | dragOp = op; 107 | Gui.Cursor = dragOpCursors[(int)op]; 108 | } 109 | 110 | private void OnMouseDown(object sender, MouseButtonEventArgs e) 111 | { 112 | if (e.Button == MouseButton.Left) 113 | { 114 | if (moveClickRegion.Contains(e.Position)) 115 | StartDragOperation (DragOperation.Move, e.Position); 116 | else if(e.X < skin.Border.Left) 117 | { 118 | if (e.Y < skin.Border.Top) 119 | StartDragOperation (DragOperation.ResizeNW, e.Position); 120 | else if (e.Y >= outer.Height - skin.Border.Bottom) 121 | StartDragOperation (DragOperation.ResizeSW, e.Position); 122 | else 123 | StartDragOperation (DragOperation.ResizeW, e.Position); 124 | } 125 | else if (e.X >= outer.Width - skin.Border.Right) 126 | { 127 | if (e.Y < skin.Border.Top) 128 | StartDragOperation (DragOperation.ResizeNE, e.Position); 129 | else if (e.Y >= outer.Height - skin.Border.Bottom) 130 | StartDragOperation (DragOperation.ResizeSE, e.Position); 131 | else 132 | StartDragOperation (DragOperation.ResizeE, e.Position); 133 | } 134 | else if (e.Y < skin.Border.Top) 135 | StartDragOperation (DragOperation.ResizeN, e.Position); 136 | else if (e.Y >= outer.Height - skin.Border.Bottom) 137 | StartDragOperation (DragOperation.ResizeS, e.Position); 138 | } 139 | 140 | justDoubleClicked = false; 141 | } 142 | 143 | private void OnMouseUp(object sender, MouseButtonEventArgs e) 144 | { 145 | if (e.Button == MouseButton.Left) 146 | { 147 | if (isDragged) 148 | { 149 | isDragged = false; 150 | Gui.Cursor = GLCursor.Default; 151 | } 152 | var now = DateTime.Now; 153 | if ((now - lastClick).TotalMilliseconds < 500.0) 154 | OnMouseDoubleClick(this, e); 155 | lastClick = now; 156 | } 157 | } 158 | 159 | private void OnMouseMove(object sender, MouseEventArgs e) 160 | { 161 | if (isDragged) 162 | { 163 | Point p = e.Position; 164 | if (Parent != null) 165 | { 166 | p.X = Math.Min(Math.Max(p.X + outer.X, 0), Parent.Inner.Width) - outer.X; 167 | p.Y = Math.Min(Math.Max(p.Y + outer.Y, 0), Parent.Inner.Height) - outer.Y; 168 | } 169 | int dx = p.X - mouseOffset.X; 170 | int dy = p.Y - mouseOffset.Y; 171 | switch (dragOp) 172 | { 173 | case DragOperation.Move: 174 | if (maximized && !justDoubleClicked) 175 | { 176 | if (Math.Max(dx, dy) > 16) 177 | { 178 | mouseOffset.X = restoreOuter.Width / 2; 179 | Maximize(false); 180 | } 181 | } 182 | else 183 | Outer = new Rectangle(outer.X + dx, outer.Y + dy, outer.Width, outer.Height); 184 | return; 185 | case DragOperation.ResizeNW: 186 | dx = outer.Width - Math.Min(Math.Max(outer.Width - dx, sizeMin.Width), sizeMax.Width); 187 | dy = outer.Height - Math.Min(Math.Max(outer.Height - dy, minHeight), sizeMax.Height); 188 | Outer = new Rectangle(outer.X + dx, outer.Y + dy, outer.Width - dx, outer.Height - dy); 189 | return; 190 | case DragOperation.ResizeN: 191 | dy = outer.Height - Math.Min(Math.Max(outer.Height - dy, minHeight), sizeMax.Height); 192 | Outer = new Rectangle(outer.X, outer.Y + dy, outer.Width, outer.Height - dy); 193 | return; 194 | case DragOperation.ResizeNE: 195 | dx = Math.Min(Math.Max(p.X, sizeMin.Width), sizeMax.Width); 196 | dy = outer.Height - Math.Min(Math.Max(outer.Height - dy, minHeight), sizeMax.Height); 197 | Outer = new Rectangle(outer.X, outer.Y + dy, dx, outer.Height - dy); 198 | return; 199 | case DragOperation.ResizeE: 200 | dx = Math.Min(Math.Max(p.X, sizeMin.Width), sizeMax.Width); 201 | Outer = new Rectangle(outer.X, outer.Y, dx, outer.Height); 202 | return; 203 | case DragOperation.ResizeSE: 204 | dx = Math.Min(Math.Max(p.X, sizeMin.Width), sizeMax.Width); 205 | dy = Math.Min(Math.Max(p.Y, minHeight), sizeMax.Height); 206 | Outer = new Rectangle(outer.X, outer.Y, dx, dy); 207 | return; 208 | case DragOperation.ResizeS: 209 | dy = Math.Min(Math.Max(p.Y, minHeight), sizeMax.Height); 210 | Outer = new Rectangle(outer.X, outer.Y, outer.Width, dy); 211 | return; 212 | case DragOperation.ResizeSW: 213 | dx = outer.Width - Math.Min(Math.Max(outer.Width - dx, sizeMin.Width), sizeMax.Width); 214 | dy = Math.Min(Math.Max(p.Y, minHeight), sizeMax.Height); 215 | Outer = new Rectangle(outer.X + dx, outer.Y, outer.Width - dx, dy); 216 | return; 217 | case DragOperation.ResizeW: 218 | dx = outer.Width - Math.Min(Math.Max(outer.Width - dx, sizeMin.Width), sizeMax.Width); 219 | Outer = new Rectangle(outer.X + dx, outer.Y, outer.Width - dx, outer.Height); 220 | return; 221 | } 222 | } 223 | 224 | if (!AutoSize && !maximized) 225 | { 226 | if (e.X < skin.Border.Left) 227 | { 228 | if (e.Y < skin.Border.Top) 229 | Gui.Cursor = GLCursor.SizeNWSE; 230 | else if (e.Y >= outer.Height - skin.Border.Bottom) 231 | Gui.Cursor = GLCursor.SizeNESW; 232 | else 233 | Gui.Cursor = GLCursor.SizeWE; 234 | } 235 | else if (e.X >= Outer.Width - skin.Border.Right) 236 | { 237 | if (e.Y < skin.Border.Top) 238 | Gui.Cursor = GLCursor.SizeNESW; 239 | else if (e.Y >= outer.Height - skin.Border.Bottom) 240 | Gui.Cursor = GLCursor.SizeNWSE; 241 | else 242 | Gui.Cursor = GLCursor.SizeWE; 243 | } 244 | else if (e.Y < skin.Border.Top || e.Y >= outer.Height - skin.Border.Bottom) 245 | Gui.Cursor = GLCursor.SizeNS; 246 | else 247 | Gui.Cursor = GLCursor.Default; 248 | } 249 | } 250 | 251 | private void OnMouseLeave(object sender, EventArgs e) 252 | { 253 | Gui.Cursor = GLCursor.Default; 254 | } 255 | 256 | private bool justDoubleClicked = false; 257 | private bool maximized = false; 258 | private Rectangle restoreOuter; 259 | private GLAnchorStyles restoreAnchor; 260 | private void Maximize(bool maximize) 261 | { 262 | if (maximized == maximize) 263 | return; 264 | if (!maximize) // restore 265 | { 266 | maximized = false; 267 | anchor = restoreAnchor; 268 | outer = restoreOuter; 269 | Invalidate(); 270 | } 271 | else // maximize 272 | { 273 | maximized = true; 274 | restoreOuter = outer; 275 | restoreAnchor = anchor; 276 | outer = Parent.Inner; 277 | anchor = GLAnchorStyles.Left | GLAnchorStyles.Top | GLAnchorStyles.Right | GLAnchorStyles.Bottom; 278 | Invalidate(); 279 | } 280 | } 281 | 282 | private void OnMouseDoubleClick(object sender, MouseEventArgs e) 283 | { 284 | if (!AutoSize && Parent != null && moveClickRegion.Contains(e.Position)) 285 | Maximize(!maximized); 286 | 287 | Gui.Cursor = GLCursor.Default; // hack to avoid move operation cursor 288 | justDoubleClicked = true; 289 | } 290 | } 291 | } 292 | 293 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLFont/GLFontBitmap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | 5 | namespace GLGUI 6 | { 7 | public class GLFontBitmap 8 | { 9 | public Bitmap bitmap; 10 | public BitmapData bitmapData; 11 | 12 | public GLFontBitmap(string filePath) 13 | { 14 | bitmap = new Bitmap(filePath); 15 | bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); 16 | } 17 | 18 | public GLFontBitmap(Bitmap bitmap) 19 | { 20 | this.bitmap = bitmap; 21 | bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); 22 | } 23 | 24 | public void Clear32(byte r, byte g, byte b, byte a) 25 | { 26 | unsafe 27 | { 28 | byte* sourcePtr = (byte*)(bitmapData.Scan0); 29 | 30 | for (int i = 0; i < bitmapData.Height; i++) 31 | { 32 | for (int j = 0; j < bitmapData.Width; j++) 33 | { 34 | *(sourcePtr) = b; 35 | *(sourcePtr + 1) = g; 36 | *(sourcePtr + 2) = r; 37 | *(sourcePtr + 3) = a; 38 | 39 | sourcePtr += 4; 40 | } 41 | sourcePtr += bitmapData.Stride - bitmapData.Width * 4; //move to the end of the line (past unused space) 42 | } 43 | } 44 | } 45 | 46 | /// 47 | /// Returns try if the given pixel is empty (i.e. black) 48 | /// 49 | public static unsafe bool EmptyPixel(BitmapData bitmapData, int px, int py) 50 | { 51 | byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 3; 52 | return (*addr == 0 && *(addr + 1) == 0 && *(addr + 2) == 0); 53 | } 54 | 55 | /// 56 | /// Returns try if the given pixel is empty (i.e. alpha is zero) 57 | /// 58 | public static unsafe bool EmptyAlphaPixel(BitmapData bitmapData, int px, int py, byte alphaEmptyPixelTolerance) 59 | { 60 | byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4; 61 | return (*(addr + 3) <= alphaEmptyPixelTolerance); 62 | } 63 | 64 | /// 65 | /// Blits a block of a bitmap data from source to destination, using the luminance of the source to determine the 66 | /// alpha of the target. Source must be 24-bit, target must be 32-bit. 67 | /// 68 | public static void BlitMask(BitmapData source, BitmapData target, int srcPx, int srcPy, int srcW, int srcH, int px, int py) 69 | { 70 | int sourceBpp = 3; 71 | int targetBpp = 4; 72 | 73 | int targetStartX, targetEndX; 74 | int targetStartY, targetEndY; 75 | int copyW, copyH; 76 | 77 | targetStartX = Math.Max(px, 0); 78 | targetEndX = Math.Min(px + srcW, target.Width); 79 | 80 | targetStartY = Math.Max(py, 0); 81 | targetEndY = Math.Min(py + srcH, target.Height); 82 | 83 | copyW = targetEndX - targetStartX; 84 | copyH = targetEndY - targetStartY; 85 | 86 | if (copyW < 0) 87 | { 88 | return; 89 | } 90 | 91 | if (copyH < 0) 92 | { 93 | return; 94 | } 95 | 96 | int sourceStartX = srcPx + targetStartX - px; 97 | int sourceStartY = srcPy + targetStartY - py; 98 | 99 | unsafe 100 | { 101 | byte* sourcePtr = (byte*)(source.Scan0); 102 | byte* targetPtr = (byte*)(target.Scan0); 103 | 104 | 105 | byte* targetY = targetPtr + targetStartY * target.Stride; 106 | byte* sourceY = sourcePtr + sourceStartY * source.Stride; 107 | for (int y = 0; y < copyH; y++, targetY += target.Stride, sourceY += source.Stride) 108 | { 109 | 110 | byte* targetOffset = targetY + targetStartX * targetBpp; 111 | byte* sourceOffset = sourceY + sourceStartX * sourceBpp; 112 | for (int x = 0; x < copyW; x++, targetOffset += targetBpp, sourceOffset += sourceBpp) 113 | { 114 | int lume = *(sourceOffset) + *(sourceOffset + 1) + *(sourceOffset + 2); 115 | 116 | lume /= 3; 117 | 118 | if (lume > 255) 119 | lume = 255; 120 | 121 | *(targetOffset + 3) = (byte)lume; 122 | 123 | } 124 | 125 | } 126 | } 127 | } 128 | 129 | /// 130 | /// Blits from source to target. Both source and target must be 32-bit 131 | /// 132 | public static void Blit(BitmapData source, BitmapData target, Rectangle sourceRect, int px, int py) 133 | { 134 | Blit(source, target, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, px, py); 135 | } 136 | 137 | /// 138 | /// Blits from source to target. Both source and target must be 32-bit 139 | /// 140 | public static void Blit(BitmapData source, BitmapData target, int srcPx, int srcPy, int srcW, int srcH, int destX, int destY) 141 | { 142 | int bpp = 4; 143 | 144 | int targetStartX, targetEndX; 145 | int targetStartY, targetEndY; 146 | int copyW, copyH; 147 | 148 | targetStartX = Math.Max(destX, 0); 149 | targetEndX = Math.Min(destX + srcW, target.Width); 150 | 151 | targetStartY = Math.Max(destY, 0); 152 | targetEndY = Math.Min(destY + srcH, target.Height); 153 | 154 | copyW = targetEndX - targetStartX; 155 | copyH = targetEndY - targetStartY; 156 | 157 | if (copyW < 0) 158 | { 159 | return; 160 | } 161 | 162 | if (copyH < 0) 163 | { 164 | return; 165 | } 166 | 167 | int sourceStartX = srcPx + targetStartX - destX; 168 | int sourceStartY = srcPy + targetStartY - destY; 169 | 170 | unsafe 171 | { 172 | byte* sourcePtr = (byte*)(source.Scan0); 173 | byte* targetPtr = (byte*)(target.Scan0); 174 | 175 | 176 | byte* targetY = targetPtr + targetStartY * target.Stride; 177 | byte* sourceY = sourcePtr + sourceStartY * source.Stride; 178 | for (int y = 0; y < copyH; y++, targetY += target.Stride, sourceY += source.Stride) 179 | { 180 | 181 | byte* targetOffset = targetY + targetStartX * bpp; 182 | byte* sourceOffset = sourceY + sourceStartX * bpp; 183 | for (int x = 0; x < copyW*bpp; x++, targetOffset ++, sourceOffset ++) 184 | *(targetOffset) = *(sourceOffset); 185 | 186 | } 187 | } 188 | } 189 | 190 | public unsafe void PutPixel32(int px, int py, byte r, byte g, byte b, byte a) 191 | { 192 | byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4; 193 | 194 | *addr = b; 195 | *(addr + 1) = g; 196 | *(addr + 2) = r; 197 | *(addr + 3) = a; 198 | } 199 | 200 | public unsafe void GetPixel32(int px, int py, ref byte r, ref byte g, ref byte b, ref byte a) 201 | { 202 | byte* addr = (byte*)(bitmapData.Scan0) + bitmapData.Stride * py + px * 4; 203 | 204 | b = *addr; 205 | g = *(addr + 1); 206 | r = *(addr + 2); 207 | a = *(addr + 3); 208 | } 209 | 210 | public void DownScale32(int newWidth, int newHeight) 211 | { 212 | GLFontBitmap newBitmap = new GLFontBitmap(new Bitmap(newWidth, newHeight, bitmap.PixelFormat)); 213 | 214 | if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) 215 | throw new Exception("DownsScale32 only works on 32 bit images"); 216 | 217 | float xscale = (float)bitmapData.Width / newWidth; 218 | float yscale = (float)bitmapData.Height / newHeight; 219 | 220 | byte r = 0, g = 0, b = 0, a = 0; 221 | float summedR = 0f; 222 | float summedG = 0f; 223 | float summedB = 0f; 224 | float summedA = 0f; 225 | 226 | int left, right, top, bottom; //the area of old pixels covered by the new bitmap 227 | 228 | 229 | float targetStartX, targetEndX; 230 | float targetStartY, targetEndY; 231 | 232 | float leftF, rightF, topF, bottomF; //edges of new pixel in old pixel coords 233 | float weight; 234 | float weightScale = xscale * yscale; 235 | float totalColourWeight = 0f; 236 | 237 | for (int m = 0; m < newHeight; m++) 238 | { 239 | for (int n = 0; n < newWidth; n++) 240 | { 241 | 242 | leftF = n * xscale; 243 | rightF = (n + 1) * xscale; 244 | 245 | topF = m * yscale; 246 | bottomF = (m + 1) * yscale; 247 | 248 | left = (int)leftF; 249 | right = (int)rightF; 250 | 251 | top = (int)topF; 252 | bottom = (int)bottomF; 253 | 254 | if (left < 0) left = 0; 255 | if (top < 0) top = 0; 256 | if (right >= bitmapData.Width) right = bitmapData.Width - 1; 257 | if (bottom >= bitmapData.Height) bottom = bitmapData.Height - 1; 258 | 259 | summedR = 0f; 260 | summedG = 0f; 261 | summedB = 0f; 262 | summedA = 0f; 263 | totalColourWeight = 0f; 264 | 265 | for (int j = top; j <= bottom; j++) 266 | { 267 | for (int i = left; i <= right; i++) 268 | { 269 | targetStartX = Math.Max(leftF, i); 270 | targetEndX = Math.Min(rightF, i + 1); 271 | 272 | targetStartY = Math.Max(topF, j); 273 | targetEndY = Math.Min(bottomF, j + 1); 274 | 275 | weight = (targetEndX - targetStartX) * (targetEndY - targetStartY); 276 | 277 | GetPixel32(i, j, ref r, ref g, ref b, ref a); 278 | 279 | summedA += weight * a; 280 | 281 | if (a != 0) 282 | { 283 | summedR += weight * r; 284 | summedG += weight * g; 285 | summedB += weight * b; 286 | totalColourWeight += weight; 287 | } 288 | 289 | } 290 | } 291 | 292 | summedR /= totalColourWeight; 293 | summedG /= totalColourWeight; 294 | summedB /= totalColourWeight; 295 | summedA /= weightScale; 296 | 297 | if (summedR < 0) summedR = 0f; 298 | if (summedG < 0) summedG = 0f; 299 | if (summedB < 0) summedB = 0f; 300 | if (summedA < 0) summedA = 0f; 301 | 302 | if (summedR >= 256) summedR = 255; 303 | if (summedG >= 256) summedG = 255; 304 | if (summedB >= 256) summedB = 255; 305 | if (summedA >= 256) summedA = 255; 306 | 307 | newBitmap.PutPixel32(n, m, (byte)summedR, (byte)summedG, (byte)summedB, (byte)summedA); 308 | } 309 | 310 | } 311 | 312 | this.Free(); 313 | this.bitmap = newBitmap.bitmap; 314 | this.bitmapData = newBitmap.bitmapData; 315 | } 316 | 317 | public void Free() 318 | { 319 | bitmap.UnlockBits(bitmapData); 320 | bitmap.Dispose(); 321 | } 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/GLSkin.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using OpenTK.Graphics; 3 | 4 | namespace GLGUI 5 | { 6 | public class GLSkin 7 | { 8 | public struct GLFormSkin 9 | { 10 | public GLFont Font; 11 | public Color4 Color; 12 | public GLPadding Border; 13 | public Color4 BorderColor; 14 | public Color4 BackgroundColor; 15 | } 16 | 17 | public struct GLButtonSkin 18 | { 19 | public GLFont Font; 20 | public Color4 Color; 21 | public GLFontAlignment TextAlign; 22 | public GLPadding Border; 23 | public Color4 BorderColor; 24 | public Color4 BackgroundColor; 25 | } 26 | 27 | public struct GLLabelSkin 28 | { 29 | public GLFont Font; 30 | public Color4 Color; 31 | public GLFontAlignment TextAlign; 32 | public GLPadding Padding; 33 | public Color4 BackgroundColor; 34 | } 35 | 36 | public struct GLTextBoxSkin 37 | { 38 | public GLFont Font; 39 | public Color4 Color; 40 | public Color4 SelectionColor; 41 | public GLFontAlignment TextAlign; 42 | public GLPadding Border; 43 | public GLPadding Padding; 44 | public Color4 BorderColor; 45 | public Color4 BackgroundColor; 46 | } 47 | 48 | public struct GLCheckBoxSkin 49 | { 50 | public GLFont Font; 51 | public Color4 Color; 52 | public GLPadding Border; 53 | public Color4 BorderColor; 54 | public Color4 BackgroundColor; 55 | } 56 | 57 | public struct GLGroupLayoutSkin 58 | { 59 | public GLPadding Border; 60 | public Color4 BorderColor; 61 | public Color4 BackgroundColor; 62 | } 63 | 64 | public struct GLFlowLayoutSkin 65 | { 66 | public GLPadding Padding; 67 | public GLPadding Border; 68 | public Color4 BorderColor; 69 | public Color4 BackgroundColor; 70 | public int Space; 71 | } 72 | 73 | public struct GLSplitLayoutSkin 74 | { 75 | public Color4 BackgroundColor; 76 | public int SplitterSize; 77 | } 78 | 79 | public struct GLSliderSkin 80 | { 81 | public Color4 KnobColor; 82 | public Color4 BackgroundColor; 83 | } 84 | 85 | public struct GLScrollableControlSkin 86 | { 87 | public GLPadding Border; 88 | public Color4 BorderColor; 89 | public Color4 BackgroundColor; 90 | } 91 | 92 | public GLFormSkin FormActive = new GLFormSkin(); 93 | public GLFormSkin FormInactive = new GLFormSkin(); 94 | 95 | public GLButtonSkin ButtonEnabled = new GLButtonSkin(); 96 | public GLButtonSkin ButtonDisabled = new GLButtonSkin(); 97 | public GLButtonSkin ButtonHover = new GLButtonSkin(); 98 | public GLButtonSkin ButtonPressed = new GLButtonSkin(); 99 | 100 | public GLLabelSkin LabelEnabled = new GLLabelSkin(); 101 | public GLLabelSkin LabelDisabled = new GLLabelSkin(); 102 | 103 | public GLLabelSkin LinkLabelEnabled = new GLLabelSkin(); 104 | public GLLabelSkin LinkLabelDisabled = new GLLabelSkin(); 105 | 106 | public GLTextBoxSkin TextBoxEnabled = new GLTextBoxSkin(); 107 | public GLTextBoxSkin TextBoxActive = new GLTextBoxSkin(); 108 | public GLTextBoxSkin TextBoxHover = new GLTextBoxSkin(); 109 | public GLTextBoxSkin TextBoxDisabled = new GLTextBoxSkin(); 110 | 111 | public GLCheckBoxSkin CheckBoxEnabled = new GLCheckBoxSkin(); 112 | public GLCheckBoxSkin CheckBoxPressed = new GLCheckBoxSkin(); 113 | public GLCheckBoxSkin CheckBoxHover = new GLCheckBoxSkin(); 114 | public GLCheckBoxSkin CheckBoxDisabled = new GLCheckBoxSkin(); 115 | 116 | public GLGroupLayoutSkin GroupLayout = new GLGroupLayoutSkin(); 117 | 118 | public GLFlowLayoutSkin FlowLayout = new GLFlowLayoutSkin(); 119 | 120 | public GLSplitLayoutSkin SplitLayout = new GLSplitLayoutSkin(); 121 | 122 | public GLSliderSkin SliderEnabled = new GLSliderSkin(); 123 | public GLSliderSkin SliderDisabled = new GLSliderSkin(); 124 | public GLSliderSkin SliderHover = new GLSliderSkin(); 125 | public GLSliderSkin SliderPressed = new GLSliderSkin(); 126 | 127 | public GLScrollableControlSkin ScrollableControl = new GLScrollableControlSkin(); 128 | 129 | public GLFlowLayoutSkin ContextMenu = new GLFlowLayoutSkin(); 130 | 131 | public GLButtonSkin ContextMenuEntryEnabled = new GLButtonSkin(); 132 | public GLButtonSkin ContextMenuEntryDisabled = new GLButtonSkin(); 133 | public GLButtonSkin ContextMenuEntryHover = new GLButtonSkin(); 134 | public GLButtonSkin ContextMenuEntryPressed = new GLButtonSkin(); 135 | 136 | public GLSkin(GLFont defaultFont = null) 137 | { 138 | if(defaultFont == null) 139 | defaultFont = new GLFont(new Font("Arial", 8.0f)); 140 | 141 | FormActive.Font = defaultFont; 142 | FormActive.Color = Color.FromArgb(240, 240, 240); 143 | FormActive.Border = new GLPadding(2); 144 | FormActive.BorderColor = Color.FromArgb(192, 56, 56, 56); 145 | FormActive.BackgroundColor = Color.FromArgb(41, 41, 41); 146 | 147 | FormInactive.Font = defaultFont; 148 | FormInactive.Color = Color.FromArgb(160, 160, 160); 149 | FormInactive.Border = new GLPadding(2); 150 | FormInactive.BorderColor = Color.FromArgb(192, 56, 56, 56); 151 | FormInactive.BackgroundColor = Color.FromArgb(41, 41, 41); 152 | 153 | 154 | ButtonEnabled.Font = defaultFont; 155 | ButtonEnabled.Color = Color.FromArgb(240, 240, 240); 156 | ButtonEnabled.TextAlign = GLFontAlignment.Centre; 157 | ButtonEnabled.Border = new GLPadding(2); 158 | ButtonEnabled.BorderColor = Color.FromArgb(56, 56, 56); 159 | ButtonEnabled.BackgroundColor = Color.Transparent; 160 | 161 | ButtonDisabled.Font = defaultFont; 162 | ButtonDisabled.Color = Color.FromArgb(128, 128, 128); 163 | ButtonDisabled.TextAlign = GLFontAlignment.Centre; 164 | ButtonDisabled.Border = new GLPadding(2); 165 | ButtonDisabled.BorderColor = Color.FromArgb(56, 56, 56); 166 | ButtonDisabled.BackgroundColor = Color.Transparent; 167 | 168 | ButtonHover.Font = defaultFont; 169 | ButtonHover.Color = Color.FromArgb(255, 255, 255); 170 | ButtonHover.TextAlign = GLFontAlignment.Centre; 171 | ButtonHover.Border = new GLPadding(2); 172 | ButtonHover.BorderColor = Color.FromArgb(64, 64, 64); 173 | ButtonHover.BackgroundColor = Color.Transparent; 174 | 175 | ButtonPressed.Font = defaultFont; 176 | ButtonPressed.Color = Color.FromArgb(96, 96, 96); 177 | ButtonPressed.TextAlign = GLFontAlignment.Centre; 178 | ButtonPressed.Border = new GLPadding(2); 179 | ButtonPressed.BorderColor = Color.FromArgb(32, 32, 32); 180 | ButtonPressed.BackgroundColor = Color.Transparent; 181 | 182 | 183 | LabelEnabled.Font = defaultFont; 184 | LabelEnabled.Color = Color.FromArgb(192, 192, 192); 185 | LabelEnabled.TextAlign = GLFontAlignment.Left; 186 | LabelEnabled.Padding = new GLPadding(1, 1, 1, 1); 187 | LabelEnabled.BackgroundColor = Color.Transparent; 188 | 189 | LabelDisabled.Font = defaultFont; 190 | LabelDisabled.Color = Color.FromArgb(128, 128, 128); 191 | LabelDisabled.TextAlign = GLFontAlignment.Left; 192 | LabelDisabled.Padding = new GLPadding(1, 1, 1, 1); 193 | LabelDisabled.BackgroundColor = Color.Transparent; 194 | 195 | 196 | LinkLabelEnabled.Font = defaultFont; 197 | LinkLabelEnabled.Color = Color.FromArgb(128, 128, 255); 198 | LinkLabelEnabled.TextAlign = GLFontAlignment.Left; 199 | LinkLabelEnabled.Padding = new GLPadding(1, 1, 1, 1); 200 | LinkLabelEnabled.BackgroundColor = Color.Transparent; 201 | 202 | LinkLabelDisabled.Font = defaultFont; 203 | LinkLabelDisabled.Color = Color.FromArgb(96, 96, 192); 204 | LinkLabelDisabled.TextAlign = GLFontAlignment.Left; 205 | LinkLabelDisabled.Padding = new GLPadding(1, 1, 1, 1); 206 | LinkLabelDisabled.BackgroundColor = Color.Transparent; 207 | 208 | 209 | TextBoxEnabled.Font = defaultFont; 210 | TextBoxEnabled.Color = Color.FromArgb(192, 192, 192); 211 | TextBoxEnabled.SelectionColor = Color.FromArgb(80, 80, 80); 212 | TextBoxEnabled.TextAlign = GLFontAlignment.Left; 213 | TextBoxEnabled.Border = new GLPadding(1); 214 | TextBoxEnabled.Padding = new GLPadding(1, 0, 1, 2); 215 | TextBoxEnabled.BorderColor = Color.FromArgb(96, 96, 96); 216 | TextBoxEnabled.BackgroundColor = Color.FromArgb(56, 56, 56); 217 | 218 | TextBoxActive.Font = defaultFont; 219 | TextBoxActive.Color = Color.FromArgb(192, 192, 192); 220 | TextBoxActive.SelectionColor = Color.FromArgb(96, 96, 96); 221 | TextBoxActive.TextAlign = GLFontAlignment.Left; 222 | TextBoxActive.Border = new GLPadding(1); 223 | TextBoxActive.Padding = new GLPadding(1, 0, 1, 2); 224 | TextBoxActive.BorderColor = Color.FromArgb(255, 192, 96); 225 | TextBoxActive.BackgroundColor = Color.FromArgb(56, 56, 56); 226 | 227 | TextBoxHover.Font = defaultFont; 228 | TextBoxHover.Color = Color.FromArgb(192, 192, 192); 229 | TextBoxHover.SelectionColor = Color.FromArgb(80, 80, 80); 230 | TextBoxHover.TextAlign = GLFontAlignment.Left; 231 | TextBoxHover.Border = new GLPadding(1); 232 | TextBoxHover.Padding = new GLPadding(1, 0, 1, 2); 233 | TextBoxHover.BorderColor = Color.FromArgb(128, 128, 128); 234 | TextBoxHover.BackgroundColor = Color.FromArgb(56, 56, 56); 235 | 236 | TextBoxDisabled.Font = defaultFont; 237 | TextBoxDisabled.Color = Color.FromArgb(128, 128, 128); 238 | TextBoxDisabled.SelectionColor = Color.FromArgb(80, 80, 80); 239 | TextBoxDisabled.TextAlign = GLFontAlignment.Left; 240 | TextBoxDisabled.Border = new GLPadding(1); 241 | TextBoxDisabled.Padding = new GLPadding(1, 0, 1, 2); 242 | TextBoxDisabled.BorderColor = Color.FromArgb(128, 128, 128); 243 | TextBoxDisabled.BackgroundColor = Color.FromArgb(56, 56, 56); 244 | 245 | 246 | CheckBoxEnabled.Font = defaultFont; 247 | CheckBoxEnabled.Color = Color.FromArgb(192, 192, 192); 248 | CheckBoxEnabled.Border = new GLPadding(1); 249 | CheckBoxEnabled.BorderColor = Color.FromArgb(96, 96, 96); 250 | CheckBoxEnabled.BackgroundColor = Color.FromArgb(56, 56, 56); 251 | 252 | CheckBoxPressed.Font = defaultFont; 253 | CheckBoxPressed.Color = Color.FromArgb(192, 192, 192); 254 | CheckBoxPressed.Border = new GLPadding(1); 255 | CheckBoxPressed.BorderColor = Color.FromArgb(255, 192, 96); 256 | CheckBoxPressed.BackgroundColor = Color.FromArgb(56, 56, 56); 257 | 258 | CheckBoxHover.Font = defaultFont; 259 | CheckBoxHover.Color = Color.FromArgb(192, 192, 192); 260 | CheckBoxHover.Border = new GLPadding(1); 261 | CheckBoxHover.BorderColor = Color.FromArgb(128, 128, 128); 262 | CheckBoxHover.BackgroundColor = Color.FromArgb(56, 56, 56); 263 | 264 | CheckBoxDisabled.Font = defaultFont; 265 | CheckBoxDisabled.Color = Color.FromArgb(128, 128, 128); 266 | CheckBoxDisabled.Border = new GLPadding(1); 267 | CheckBoxDisabled.BorderColor = Color.FromArgb(128, 128, 128); 268 | CheckBoxDisabled.BackgroundColor = Color.FromArgb(56, 56, 56); 269 | 270 | 271 | GroupLayout.Border = new GLPadding(1); 272 | GroupLayout.BorderColor = Color.Transparent;//Color.FromArgb(96, 96, 96); 273 | GroupLayout.BackgroundColor = Color.Transparent;//Color.FromArgb(240, 240, 240); 274 | 275 | 276 | FlowLayout.Padding = new GLPadding(2); 277 | FlowLayout.Border = new GLPadding(0); 278 | FlowLayout.BorderColor = Color.Transparent; 279 | FlowLayout.BackgroundColor = Color.Transparent; 280 | FlowLayout.Space = 2; 281 | 282 | 283 | SplitLayout.BackgroundColor = Color.FromArgb(0, 0, 0); 284 | SplitLayout.SplitterSize = 1; 285 | 286 | 287 | SliderEnabled.KnobColor = Color.FromArgb(80, 80, 80); 288 | SliderEnabled.BackgroundColor = Color.FromArgb(28, 28, 28);//Color.FromArgb(56, 56, 56); 289 | 290 | SliderDisabled.KnobColor = Color.Transparent; //Color.FromArgb(96, 96, 96); 291 | SliderDisabled.BackgroundColor = Color.FromArgb(28, 28, 28);//Color.FromArgb(56, 56, 56); 292 | 293 | SliderHover.KnobColor = Color.FromArgb(96, 96, 96); 294 | SliderHover.BackgroundColor = Color.FromArgb(32, 32, 32); 295 | 296 | SliderPressed.KnobColor = Color.FromArgb(80, 80, 80); 297 | SliderPressed.BackgroundColor = Color.FromArgb(32, 32, 32); 298 | 299 | 300 | ScrollableControl.Border = new GLPadding(1); 301 | ScrollableControl.BorderColor = Color.FromArgb(56, 56, 56); 302 | ScrollableControl.BackgroundColor = Color.FromArgb(41, 41, 41); 303 | 304 | 305 | ContextMenu.Padding = new GLPadding(1); 306 | ContextMenu.Border = new GLPadding(1); 307 | ContextMenu.BorderColor = Color.FromArgb(128, 128, 128); 308 | ContextMenu.BackgroundColor = Color.FromArgb(32, 32, 32); 309 | ContextMenu.Space = 1; 310 | 311 | 312 | ContextMenuEntryEnabled.Font = defaultFont; 313 | ContextMenuEntryEnabled.Color = Color.FromArgb(240, 240, 240); 314 | ContextMenuEntryEnabled.TextAlign = GLFontAlignment.Left; 315 | ContextMenuEntryEnabled.Border = new GLPadding(2); 316 | ContextMenuEntryEnabled.BorderColor = Color.FromArgb(56, 56, 56); 317 | ContextMenuEntryEnabled.BackgroundColor = Color.Transparent; 318 | 319 | ContextMenuEntryDisabled.Font = defaultFont; 320 | ContextMenuEntryDisabled.Color = Color.FromArgb(128, 128, 128); 321 | ContextMenuEntryDisabled.TextAlign = GLFontAlignment.Left; 322 | ContextMenuEntryDisabled.Border = new GLPadding(2); 323 | ContextMenuEntryDisabled.BorderColor = Color.FromArgb(56, 56, 56); 324 | ContextMenuEntryDisabled.BackgroundColor = Color.Transparent; 325 | 326 | ContextMenuEntryHover.Font = defaultFont; 327 | ContextMenuEntryHover.Color = Color.FromArgb(255, 255, 255); 328 | ContextMenuEntryHover.TextAlign = GLFontAlignment.Left; 329 | ContextMenuEntryHover.Border = new GLPadding(2); 330 | ContextMenuEntryHover.BorderColor = Color.FromArgb(64, 64, 64); 331 | ContextMenuEntryHover.BackgroundColor = Color.Transparent; 332 | 333 | ContextMenuEntryPressed.Font = defaultFont; 334 | ContextMenuEntryPressed.Color = Color.FromArgb(96, 96, 96); 335 | ContextMenuEntryPressed.TextAlign = GLFontAlignment.Left; 336 | ContextMenuEntryPressed.Border = new GLPadding(2); 337 | ContextMenuEntryPressed.BorderColor = Color.FromArgb(32, 32, 32); 338 | ContextMenuEntryPressed.BackgroundColor = Color.Transparent; 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /GLGUI/GLGUI/Advanced/GLDataControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace GLGUI.Advanced 8 | { 9 | public class GLDataControl : GLScrollableControl 10 | { 11 | public GLSkin.GLLabelSkin LabelSkin { get { return labelSkin; } set { labelSkin = value; if(history.Count > 0) SetData(history.First()); } } 12 | public GLSkin.GLLabelSkin LinkLabelSkin { get { return linkLabelSkin; } set { linkLabelSkin = value; if (history.Count > 0) SetData(history.First()); } } 13 | 14 | public Dictionary Hidden = new Dictionary(); 15 | public Dictionary>[]> Links = new Dictionary>[]>(); 16 | public Dictionary> Shortcuts = new Dictionary>(); 17 | 18 | private int row; 19 | private Stack> history = new Stack>(); 20 | private List, GLLabel>> updateData = new List, GLLabel>>(256); 21 | 22 | private GLFlowLayout horizontal, left, right; 23 | private GLSkin.GLLabelSkin labelSkin, linkLabelSkin; 24 | 25 | public GLDataControl(GLGui gui) : base(gui) 26 | { 27 | Render += (s, d) => UpdateData(); 28 | 29 | horizontal = Add(new GLFlowLayout(gui) { FlowDirection = GLFlowDirection.LeftToRight, AutoSize = true }); 30 | left = horizontal.Add(new GLFlowLayout(gui) { FlowDirection = GLFlowDirection.TopDown, AutoSize = true }); 31 | var skin = left.Skin; 32 | skin.Space = 0; 33 | left.Skin = skin; 34 | right = horizontal.Add(new GLFlowLayout(gui) { FlowDirection = GLFlowDirection.TopDown, AutoSize = true }); 35 | right.Skin = skin; 36 | 37 | labelSkin = gui.Skin.LabelEnabled; 38 | linkLabelSkin = gui.Skin.LinkLabelEnabled; 39 | 40 | // defaults 41 | Hidden.Add(typeof(IEnumerable), new string[] { 42 | "_items", "_size", "_version", "_syncRoot", 43 | "m_buckets", "m_slots", "m_count", "m_lastIndex", "m_freeList", "m_comparer", 44 | "m_version", "m_siInfo", "m_collection", "m_boundedCapacity", "m_freeNodes", 45 | "m_occupiedNodes", "m_isDisposed", "m_ConsumersCancellationTokenSource", 46 | "m_ProducersCancellationTokenSource", "m_currentAdders", 47 | "buckets", "entries", "count", "version", "freeList", "freeCount", "comparer", "keys", "values", 48 | "IsFixedSize", "IsReadOnly", "IsSynchronized", "SyncRoot" }); 49 | Hidden.Add(typeof(Array), new string[] { "LongLength", "Rank", "Count" }); 50 | Hidden.Add(typeof(KeyValuePair<,>), new string[] { "key", "value" }); 51 | Hidden.Add(typeof(Dictionary<,>), new string[] { "Keys", "Values" }); 52 | } 53 | 54 | private void AddRow(string name, EventHandler click, Func value) 55 | { 56 | var link = left.Add(new GLLinkLabel(Gui) { AutoSize = true, Text = name, SkinEnabled = linkLabelSkin }); 57 | if (value() != null) 58 | link.Click += click; 59 | var label = right.Add(new GLLabel(Gui) { AutoSize = true, Text = (value() ?? "null").ToString(), SkinEnabled = labelSkin }); 60 | updateData.Add(new Tuple, GLLabel>(value, label)); 61 | row++; 62 | } 63 | 64 | public void SetData(object obj) 65 | { 66 | setData(() => obj, true); 67 | } 68 | 69 | private List getRelated(Dictionary dict, Type type) 70 | { 71 | List list = new List(); 72 | 73 | while (type != null) 74 | { 75 | if (dict.ContainsKey(type)) 76 | list.AddRange(dict[type]); 77 | 78 | if (type.IsGenericType) 79 | { 80 | Type genType = type.GetGenericTypeDefinition(); 81 | if (dict.ContainsKey(genType)) 82 | list.AddRange(dict[genType]); 83 | } 84 | 85 | var interfaces = type.GetInterfaces(); 86 | foreach(var iface in interfaces) 87 | { 88 | if (dict.ContainsKey(iface)) 89 | list.AddRange(dict[iface]); 90 | 91 | if (iface.IsGenericType) 92 | { 93 | Type ifaceGenType = iface.GetGenericTypeDefinition(); 94 | if (dict.ContainsKey(ifaceGenType)) 95 | list.AddRange(dict[ifaceGenType]); 96 | } 97 | } 98 | 99 | type = type.BaseType; 100 | } 101 | 102 | return list; 103 | } 104 | 105 | private void considerShortcutsAndAdd(string name, Func obj) 106 | { 107 | Func value = obj; 108 | var o = obj(); 109 | if (o != null) 110 | { 111 | Type type = o.GetType(); 112 | Func shortcut; 113 | 114 | while (type != null) 115 | { 116 | if (Shortcuts.TryGetValue(type, out shortcut)) 117 | { 118 | value = () => shortcut(obj()); 119 | break; 120 | } 121 | if (type.IsGenericType) 122 | { 123 | Type genType = type.GetGenericTypeDefinition(); 124 | if (Shortcuts.TryGetValue(genType, out shortcut)) 125 | { 126 | value = () => shortcut(obj()); 127 | break; 128 | } 129 | } 130 | var interfaces = type.GetInterfaces(); 131 | foreach (var iface in interfaces) 132 | { 133 | if (Shortcuts.TryGetValue(iface, out shortcut)) 134 | { 135 | value = () => shortcut(obj()); 136 | goto shortcutFound; 137 | } 138 | if (iface.IsGenericType) 139 | { 140 | Type ifaceGenType = iface.GetGenericTypeDefinition(); 141 | if (Shortcuts.TryGetValue(ifaceGenType, out shortcut)) 142 | { 143 | value = () => shortcut(obj()); 144 | goto shortcutFound; 145 | } 146 | } 147 | } 148 | type = type.BaseType; 149 | } 150 | } 151 | 152 | shortcutFound: 153 | AddRow(name, (s, e) => setData(value, false), value); 154 | } 155 | 156 | private void setData(Func obj, bool resetHistory) 157 | { 158 | left.Clear(); 159 | right.Clear(); 160 | Horizontal.Value = 0.0f; 161 | Vertical.Value = 0.0f; 162 | updateData.Clear(); 163 | 164 | if (resetHistory) 165 | history.Clear(); 166 | 167 | row = 0; 168 | 169 | if (history.Count > 0) 170 | { 171 | AddRow("[..]", (s, e) => 172 | { 173 | if (history.Count < 2) 174 | { 175 | setData(null, true); 176 | return; 177 | } 178 | history.Pop(); 179 | setData(history.Pop(), false); 180 | }, history.Peek()); 181 | } 182 | 183 | if (obj == null || obj() == null) 184 | return; 185 | 186 | try 187 | { 188 | Type type = obj().GetType(); 189 | Type rtype = type.BaseType; 190 | 191 | var hidden = getRelated(Hidden, type); 192 | var links = getRelated>>(Links, type); 193 | 194 | // fields 195 | rtype = type.BaseType; 196 | var fields = new Dictionary(); 197 | foreach (var f in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 198 | { 199 | string name = f.Name.Split('.').Last(); 200 | if (!fields.ContainsKey(name)) 201 | fields.Add(name, f); 202 | } 203 | while (rtype != null) 204 | { 205 | var addFields = rtype.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); 206 | foreach (var af in addFields) 207 | { 208 | string name = af.Name.Split('.').Last(); 209 | if (!fields.ContainsKey(name)) 210 | fields.Add(name, af); 211 | } 212 | rtype = rtype.BaseType; 213 | } 214 | foreach (var field in fields) 215 | { 216 | if (field.Key[0] == '<') // skip property backing fields 217 | continue; 218 | if (hidden.Contains(field.Key)) 219 | continue; 220 | var f = field.Value; 221 | considerShortcutsAndAdd(field.Key, () => { try { return f.GetValue(obj()); } catch (Exception) { return null; } }); 222 | } 223 | 224 | // properties 225 | rtype = type.BaseType; 226 | var properties = new Dictionary(); 227 | foreach (var p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 228 | { 229 | string name = p.Name.Split('.').Last(); 230 | if (!properties.ContainsKey(name)) 231 | properties.Add(name, p); 232 | } 233 | while (rtype != null) 234 | { 235 | var addProperties = rtype.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic); 236 | foreach (var ap in addProperties) 237 | { 238 | string name = ap.Name.Split('.').Last(); 239 | if (!properties.ContainsKey(name)) 240 | properties.Add(name, ap); 241 | } 242 | rtype = rtype.BaseType; 243 | } 244 | foreach (var property in properties) 245 | { 246 | if (hidden.Contains(property.Key)) 247 | continue; 248 | var p = property.Value; 249 | try { p.GetValue(obj(), null); } 250 | catch (Exception) { continue; } 251 | considerShortcutsAndAdd(property.Key, () => { try { return p.GetValue(obj(), null); } catch (Exception) { return "Exception caught in get method"; } }); 252 | } 253 | 254 | // links 255 | foreach (var link in links) 256 | { 257 | Func linkObj = link.Item2; 258 | considerShortcutsAndAdd(link.Item1, () => { try { return linkObj; } catch (Exception) { return "Exception caught in link method"; } }); 259 | } 260 | 261 | // arrays and collections 262 | if (type.IsArray) 263 | { 264 | Array array = (Array)obj(); 265 | for (int i = 0; i < array.Length; i++) 266 | { 267 | int j = i; 268 | considerShortcutsAndAdd("[" + i + "]", () => { try { return ((Array)obj()).GetValue(j); } catch (Exception) { return null; } }); 269 | } 270 | } 271 | else if (type.GetInterfaces().Contains(typeof(ICollection))) 272 | { 273 | int i = 0; 274 | try 275 | { 276 | foreach (object item in ((IEnumerable)obj())) 277 | { 278 | int j = i++; 279 | considerShortcutsAndAdd("[" + i + "]", () => { try { return ((IEnumerable)obj()).Cast().ElementAt(j); } catch(Exception) { return null; } }); 280 | } 281 | } 282 | catch (InvalidOperationException) 283 | { 284 | AddRow("retry", (s, e) => { setData(history.Pop(), false); }, () => "InvalidOperationException"); 285 | } 286 | catch (IndexOutOfRangeException) 287 | { 288 | AddRow("retry", (s, e) => { setData(history.Pop(), false); }, () => "IndexOutOfRangeException"); 289 | } 290 | } 291 | } 292 | catch (Exception e) 293 | { 294 | Console.WriteLine("[GLDataControl] Unknown error occured:\n{0}", e.ToString()); 295 | SetData(null); 296 | } 297 | 298 | history.Push(obj); 299 | } 300 | 301 | MethodInfo updateLayout = typeof(GLLabel).GetMethod("UpdateLayout", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic); 302 | object[] updateParams = new object[0]; 303 | 304 | public void UpdateData() 305 | { 306 | Gui.SuspendLayout(); 307 | bool changed = false; 308 | foreach(var u in updateData) 309 | { 310 | try 311 | { 312 | string text = (u.Item1() ?? "null").ToString(); 313 | if (u.Item2.Text != text) 314 | { 315 | u.Item2.Text = text; 316 | updateLayout.Invoke(u.Item2, updateParams); 317 | changed = true; 318 | } 319 | } 320 | catch(Exception) 321 | { 322 | if (history.Count > 1) 323 | { 324 | history.Pop(); 325 | setData(history.Pop(), false); 326 | } 327 | else 328 | { 329 | setData(null, true); 330 | } 331 | Gui.ResumeLayout(); 332 | return; 333 | } 334 | } 335 | Gui.ResumeLayout(); 336 | if(changed) 337 | right.Invalidate(); 338 | } 339 | } 340 | } 341 | --------------------------------------------------------------------------------