From 9fcdbc3637d8d0932a48ee85560034b99c55aa12 Mon Sep 17 00:00:00 2001
From: onepiecefreak3 <realonepiecefreak@gmx.de>
Date: Fri, 11 Oct 2024 21:06:50 +0200
Subject: [PATCH] Add font property to various controls; Optimize most
 components to reflect new wiki; Bump to 1.2.0;

---
 ImGui.Forms/Controls/ArrowButton.cs           |  11 +-
 ImGui.Forms/Controls/Base/Component.cs        |  24 +-
 ImGui.Forms/Controls/Button.cs                |   7 +-
 ImGui.Forms/Controls/CheckBox.cs              |  32 +-
 ImGui.Forms/Controls/ComboBox.cs              |  44 ++-
 ImGui.Forms/Controls/DropDownBox.cs           |  84 ------
 ImGui.Forms/Controls/Expander.cs              |  11 +-
 ImGui.Forms/Controls/ImageButton.cs           |   2 +-
 ImGui.Forms/Controls/Label.cs                 |   2 +-
 ImGui.Forms/Controls/Layouts/StackLayout.cs   |   8 +-
 ImGui.Forms/Controls/Layouts/TableCell.cs     |   8 +-
 ImGui.Forms/Controls/Layouts/TableLayout.cs   |  32 +-
 ImGui.Forms/Controls/Layouts/ZLayout.cs       |   4 +-
 ImGui.Forms/Controls/Lists/ActivableList.cs   |   9 +-
 ImGui.Forms/Controls/Lists/BaseList.cs        | 182 ------------
 ImGui.Forms/Controls/Lists/DataTable.cs       |  17 +-
 ImGui.Forms/Controls/Lists/ImageList.cs       | 188 ------------
 ImGui.Forms/Controls/Lists/ImageListItem.cs   |  24 --
 ImGui.Forms/Controls/Lists/List.cs            | 276 +++++++++++++++++-
 ImGui.Forms/Controls/Menu/MenuBar.cs          |  20 +-
 ImGui.Forms/Controls/Menu/MenuBarButton.cs    |  24 +-
 ImGui.Forms/Controls/Menu/MenuBarCheckBox.cs  |  22 +-
 ImGui.Forms/Controls/Menu/MenuBarMenu.cs      |  33 ++-
 ImGui.Forms/Controls/Menu/MenuBarRadio.cs     |  18 +-
 ImGui.Forms/Controls/MultiLineTextBox.cs      |   7 +-
 ImGui.Forms/Controls/PictureBox.cs            |  17 +-
 ImGui.Forms/Controls/ProgressBar.cs           |   5 +-
 ImGui.Forms/Controls/TabControl.cs            |   4 +-
 ImGui.Forms/Controls/TextBox.cs               |  10 +-
 ImGui.Forms/Controls/Tree/TreeNode.cs         |   2 +-
 ImGui.Forms/Controls/ZoomablePictureBox.cs    |   7 +-
 ImGui.Forms/Form.cs                           |   6 +-
 ImGui.Forms/ImGui.Forms.nuspec                |   2 +-
 ImGui.Forms/Localization/BaseLocalizer.cs     |   6 +
 ImGui.Forms/Localization/ILocalizer.cs        |   1 +
 ImGui.Forms/Modals/IO/ComboInputBox.cs        |  55 ++--
 ImGui.Forms/Modals/IO/InputBox.cs             |  24 +-
 ImGui.Forms/Modals/IO/OpenFileDialog.cs       |  54 ++--
 ImGui.Forms/Modals/IO/SaveFileDialog.cs       |  47 +--
 ImGui.Forms/Modals/IO/SelectFolderDialog.cs   |  26 +-
 ImGui.Forms/Modals/MessageBox.cs              |  46 ++-
 ImGui.Forms/Modals/Modal.cs                   |  31 +-
 .../Resources/LocalizationResources.cs        |  63 ++++
 ImGui.Forms/Resources/TextMeasurer.cs         |   1 -
 44 files changed, 728 insertions(+), 768 deletions(-)
 delete mode 100644 ImGui.Forms/Controls/DropDownBox.cs
 delete mode 100644 ImGui.Forms/Controls/Lists/BaseList.cs
 delete mode 100644 ImGui.Forms/Controls/Lists/ImageList.cs
 delete mode 100644 ImGui.Forms/Controls/Lists/ImageListItem.cs
 create mode 100644 ImGui.Forms/Resources/LocalizationResources.cs

diff --git a/ImGui.Forms/Controls/ArrowButton.cs b/ImGui.Forms/Controls/ArrowButton.cs
index f3563b8..e707847 100644
--- a/ImGui.Forms/Controls/ArrowButton.cs
+++ b/ImGui.Forms/Controls/ArrowButton.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Numerics;
 using ImGui.Forms.Controls.Base;
 using ImGui.Forms.Models;
 using ImGui.Forms.Models.IO;
@@ -18,6 +19,8 @@ public class ArrowButton : Component
 
         public ImGuiDir Direction { get; set; }
 
+        public Vector2 Padding { get; set; } = new(4, 3);
+
         #endregion
 
         #region Events
@@ -33,15 +36,15 @@ public ArrowButton(ImGuiDir direction = ImGuiDir.None)
 
         public override Size GetSize()
         {
-            var padding = ImGuiNET.ImGui.GetStyle().FramePadding;
-
-            return new Size((int)Math.Ceiling(ButtonSizeX_ + padding.X * 2), (int)Math.Ceiling(ButtonSizeY_ + padding.Y * 2));
+            return new Size((int)Math.Ceiling(ButtonSizeX_ + Padding.X * 2), (int)Math.Ceiling(ButtonSizeY_ + Padding.Y * 2));
         }
 
         protected override void UpdateInternal(Rectangle contentRect)
         {
             var enabled = Enabled;
 
+            ImGuiNET.ImGui.PushStyleVar(ImGuiStyleVar.FramePadding, Padding);
+
             if (!enabled)
             {
                 ImGuiNET.ImGui.PushStyleColor(ImGuiCol.Button, ImGuiNET.ImGui.GetColorU32(ImGuiCol.TextDisabled));
@@ -54,6 +57,8 @@ protected override void UpdateInternal(Rectangle contentRect)
 
             if (!enabled)
                 ImGuiNET.ImGui.PopStyleColor(3);
+
+            ImGuiNET.ImGui.PopStyleVar();
         }
 
         private void OnClicked()
diff --git a/ImGui.Forms/Controls/Base/Component.cs b/ImGui.Forms/Controls/Base/Component.cs
index 54eebcf..65d5497 100644
--- a/ImGui.Forms/Controls/Base/Component.cs
+++ b/ImGui.Forms/Controls/Base/Component.cs
@@ -9,11 +9,12 @@
 
 namespace ImGui.Forms.Controls.Base
 {
-    // TODO: Due to executive inconsistency, remove ApplyStyles/RemoveStyles methods and apply styles in Update/Size methods directly
     public abstract class Component
     {
         private bool _tabInactive;
 
+        #region Properties
+
         /// <summary>
         /// The Id for this component.
         /// </summary>
@@ -40,6 +41,8 @@ public abstract class Component
         /// </summary>
         public bool ShowBorder { get; set; }
 
+        #endregion
+
         #region Events
 
         /// <summary>
@@ -119,13 +122,14 @@ protected bool IsTabInactiveCore()
         /// Gets the finalized width of this component.
         /// </summary>
         /// <param name="parentWidth">The width of the parent component.</param>
+        /// <param name="parentHeight">The height of the parent component.</param>
         /// <param name="layoutCorrection">A correctional value for layouts.</param>
         /// <returns>The finalized width of this component.</returns>
-        public int GetWidth(int parentWidth, float layoutCorrection = 1f)
+        public int GetWidth(int parentWidth, int parentHeight, float layoutCorrection = 1f)
         {
             var width = GetSize().Width;
             return width.IsContentAligned ?
-                GetContentWidth(parentWidth, layoutCorrection) :
+                GetContentWidth(parentWidth, parentHeight, layoutCorrection) :
                 GetDimension(width, parentWidth, layoutCorrection);
         }
 
@@ -133,37 +137,39 @@ public int GetWidth(int parentWidth, float layoutCorrection = 1f)
         /// Gets the width of this component, if it's content aligned.
         /// </summary>
         /// <param name="parentWidth">The width of the parent component.</param>
+        /// <param name="parentHeight">The height of the parent component.</param>
         /// <param name="layoutCorrection">A correctional value for layouts.</param>
         /// <returns>The content width of this component.</returns>
-        protected virtual int GetContentWidth(int parentWidth, float layoutCorrection = 1f) => 0;
+        protected virtual int GetContentWidth(int parentWidth, int parentHeight, float layoutCorrection = 1f) => 0;
 
         /// <summary>
         /// Gets the finalized height of this component.
         /// </summary>
+        /// <param name="parentWidth">The width of the parent component.</param>
         /// <param name="parentHeight">The height of the parent component.</param>
         /// <param name="layoutCorrection">A correctional value for layouts.</param>
         /// <returns>The finalized height of this component.</returns>
-        public int GetHeight(int parentHeight, float layoutCorrection = 1f)
+        public int GetHeight(int parentWidth, int parentHeight, float layoutCorrection = 1f)
         {
             var height = GetSize().Height;
             return height.IsContentAligned ?
-                GetContentHeight(parentHeight, layoutCorrection) :
+                GetContentHeight(parentWidth, parentHeight, layoutCorrection) :
                 GetDimension(height, parentHeight, layoutCorrection);
         }
 
         /// <summary>
         /// Gets the height of this component, if it's content aligned.
         /// </summary>
+        /// <param name="parentWidth">The width of the parent component.</param>
         /// <param name="parentHeight">The height of the parent component.</param>
         /// <param name="layoutCorrection">A correctional value for layouts.</param>
         /// <returns>The content height of this component.</returns>
-        protected virtual int GetContentHeight(int parentHeight, float layoutCorrection = 1f) => 0;
+        protected virtual int GetContentHeight(int parentWidth, int parentHeight, float layoutCorrection = 1f) => 0;
 
         /// <summary>
         /// Gets the absolute or relative size of the component. Use <see cref="GetWidth"/> or <see cref="GetHeight"/> to get the finalized size.
         /// </summary>
         /// <returns>The absolute or relative size of this component.</returns>
-        /// TODO: Make gettable property, default value Size.Content
         public abstract Size GetSize();
 
         /// <summary>
@@ -243,7 +249,7 @@ private void OnDragDrop(DragDropEvent[] events)
         /// <param name="maxDimensionValue">The maximum to calculate relative <see cref="SizeValue"/>s against.</param>
         /// <param name="correction">The corrective value to calculate relative <see cref="SizeValue"/>s against.</param>
         /// <returns></returns>
-        internal static int GetDimension(SizeValue dimensionValue, int maxDimensionValue, float correction = 1f)
+        protected internal static int GetDimension(SizeValue dimensionValue, int maxDimensionValue, float correction = 1f)
         {
             if (dimensionValue.IsAbsolute)
                 return (int)Math.Min(dimensionValue.Value, maxDimensionValue);
diff --git a/ImGui.Forms/Controls/Button.cs b/ImGui.Forms/Controls/Button.cs
index 074029d..4f9493c 100644
--- a/ImGui.Forms/Controls/Button.cs
+++ b/ImGui.Forms/Controls/Button.cs
@@ -16,6 +16,7 @@ public class Button : Component
 
         public LocalizedString Text { get; set; }
         public LocalizedString Tooltip { get; set; }
+        public FontResource Font { get; set; }
 
         public KeyCommand KeyAction { get; set; }
 
@@ -23,8 +24,6 @@ public class Button : Component
 
         public SizeValue Width { get; set; } = SizeValue.Content;
 
-        public FontResource Font { get; set; }
-
         #endregion
 
         #region Events
@@ -58,7 +57,7 @@ protected override void UpdateInternal(Rectangle contentRect)
 
             ApplyStyles(enabled, font);
 
-            if ((ImGuiNET.ImGui.Button(EscapeText(), new Vector2(contentRect.Width, contentRect.Height)) || IsKeyDown(KeyAction)) && Enabled)
+            if ((ImGuiNET.ImGui.Button(EscapeText(), contentRect.Size) || IsKeyDown(KeyAction)) && Enabled)
                 OnClicked();
 
             if (Tooltip is { IsEmpty: false } && IsHoveredCore())
@@ -100,7 +99,7 @@ private void RemoveStyles(bool enabled, FontResource font)
 
         protected void OnClicked()
         {
-            Clicked?.Invoke(this, new EventArgs());
+            Clicked?.Invoke(this, EventArgs.Empty);
         }
 
         protected string EscapeText()
diff --git a/ImGui.Forms/Controls/CheckBox.cs b/ImGui.Forms/Controls/CheckBox.cs
index 9c26947..5f20530 100644
--- a/ImGui.Forms/Controls/CheckBox.cs
+++ b/ImGui.Forms/Controls/CheckBox.cs
@@ -15,12 +15,12 @@ public class CheckBox : Component
         #region Properties
 
         public LocalizedString Text { get; set; }
-
         public LocalizedString Tooltip { get; set; }
+        public FontResource Font { get; set; }
 
         public bool Checked
         {
-            get=> _checked;
+            get => _checked;
             set
             {
                 _checked = value;
@@ -43,19 +43,26 @@ public CheckBox(LocalizedString text = default)
 
         public override Size GetSize()
         {
+            ApplyStyles(Enabled, Font);
+
             var size = TextMeasurer.MeasureText(Text);
-            return new Size((int)(Math.Ceiling(size.X) + 21 + ImGuiNET.ImGui.GetStyle().ItemInnerSpacing.X), (int)Math.Max(Math.Ceiling(size.Y), 21));
+            var width = (int)(Math.Ceiling(size.X) + 21 + ImGuiNET.ImGui.GetStyle().ItemInnerSpacing.X);
+            var height = (int)Math.Max(Math.Ceiling(size.Y), 21);
+
+            RemoveStyles(Enabled, Font);
+
+            return new Size(width, height);
         }
 
         protected override void UpdateInternal(Rectangle contentRect)
         {
-            // TODO: Draw checkbox manually, when disabled, to work around checkmark flickering
             ImGuiNET.ImGui.SetNextItemWidth(contentRect.Width);
 
             var check = Checked;
             var enabled = Enabled;
+            var font = Font;
 
-            ApplyStyles(enabled);
+            ApplyStyles(enabled, font);
 
             if (IsHovering(contentRect) && !string.IsNullOrEmpty(Tooltip))
                 ImGuiNET.ImGui.SetTooltip(Tooltip);
@@ -63,10 +70,10 @@ protected override void UpdateInternal(Rectangle contentRect)
             if (ImGuiNET.ImGui.Checkbox(Text, ref check) && Enabled)
                 Checked = check;
 
-            RemoveStyles(enabled);
+            RemoveStyles(enabled, font);
         }
 
-        private void ApplyStyles(bool enabled)
+        private void ApplyStyles(bool enabled, FontResource font)
         {
             if (!enabled)
             {
@@ -75,10 +82,17 @@ private void ApplyStyles(bool enabled)
                 ImGuiNET.ImGui.PushStyleColor(ImGuiCol.FrameBgActive, ImGuiNET.ImGui.GetColorU32(ImGuiCol.TextDisabled));
                 ImGuiNET.ImGui.PushStyleColor(ImGuiCol.FrameBgHovered, ImGuiNET.ImGui.GetColorU32(ImGuiCol.TextDisabled));
             }
+
+            ImFontPtr? fontPtr = font?.GetPointer();
+            if (fontPtr != null)
+                ImGuiNET.ImGui.PushFont(fontPtr.Value);
         }
 
-        private void RemoveStyles(bool enabled)
+        private void RemoveStyles(bool enabled, FontResource font)
         {
+            if (font?.GetPointer() != null)
+                ImGuiNET.ImGui.PopFont();
+
             if (!enabled)
                 ImGuiNET.ImGui.PopStyleColor(4);
         }
@@ -90,7 +104,7 @@ private bool IsHovering(Rectangle contentRect)
 
         private void OnCheckChanged()
         {
-            CheckChanged?.Invoke(this, new EventArgs());
+            CheckChanged?.Invoke(this, EventArgs.Empty);
         }
     }
 }
diff --git a/ImGui.Forms/Controls/ComboBox.cs b/ImGui.Forms/Controls/ComboBox.cs
index 49db731..f993c40 100644
--- a/ImGui.Forms/Controls/ComboBox.cs
+++ b/ImGui.Forms/Controls/ComboBox.cs
@@ -4,6 +4,7 @@
 using System.Numerics;
 using System.Runtime.InteropServices;
 using ImGui.Forms.Controls.Base;
+using ImGui.Forms.Localization;
 using ImGui.Forms.Models;
 using ImGui.Forms.Resources;
 using ImGuiNET;
@@ -15,13 +16,24 @@ namespace ImGui.Forms.Controls
 {
     public class ComboBox<TItem> : Component
     {
+        private const int ButtonSizeX_ = 11;
+
         private string _input = string.Empty;
+        private DropDownItem<TItem> _selected;
 
         #region Properties
 
-        public IList<ComboBoxItem<TItem>> Items { get; } = new List<ComboBoxItem<TItem>>();
+        public IList<DropDownItem<TItem>> Items { get; } = new List<DropDownItem<TItem>>();
 
-        public ComboBoxItem<TItem> SelectedItem { get; set; }
+        public DropDownItem<TItem> SelectedItem
+        {
+            get => _selected;
+            set
+            {
+                _selected = value;
+                _input = value.Name;
+            }
+        }
 
         public SizeValue Width { get; set; } = SizeValue.Content;
 
@@ -46,7 +58,7 @@ public class ComboBox<TItem> : Component
         public override Size GetSize()
         {
             var maxWidth = Items.Select(x => TextMeasurer.GetCurrentLineWidth(x.Name)).DefaultIfEmpty(0).Max() + (int)ImGuiNET.ImGui.GetStyle().ItemInnerSpacing.X * 2;
-            var arrowWidth = 20;
+            int arrowWidth = (int)(ButtonSizeX_ + ImGuiNET.ImGui.GetStyle().FramePadding.X * 2);
 
             SizeValue width = Width.IsContentAligned ? maxWidth + arrowWidth : Width;
             var height = TextMeasurer.GetCurrentLineHeight() + (int)ImGuiNET.ImGui.GetStyle().ItemInnerSpacing.Y * 2;
@@ -63,10 +75,13 @@ protected override unsafe void UpdateInternal(Rectangle contentRect)
 
             ImGuiNET.ImGui.PushID(Id);
 
+            var arrowWidth = ButtonSizeX_ + ImGuiNET.ImGui.GetStyle().FramePadding.X * 2;
+            ImGuiNET.ImGui.SetNextItemWidth(contentRect.Width - arrowWidth);
+
             bool isFinal = ImGuiNET.ImGui.InputText("##in", ref _input, MaxCharacters, ImGuiInputTextFlags.CallbackAlways | ImGuiInputTextFlags.EnterReturnsTrue, Propose);
             if (isFinal)
             {
-                ComboBoxItem<TItem> selectedItem = Items.FirstOrDefault(i => i.Name == _input);
+                DropDownItem<TItem> selectedItem = Items.FirstOrDefault(i => i.Name == _input);
                 if (SelectedItem != selectedItem)
                 {
                     SelectedItem = selectedItem;
@@ -92,9 +107,7 @@ protected override unsafe void UpdateInternal(Rectangle contentRect)
             ImGuiNET.ImGui.SetNextWindowSize(size);
             if (ImGuiNET.ImGui.BeginPopup("combobox", ImGuiWindowFlags.NoMove))
             {
-                ImGuiNET.ImGui.Text("Select an item");
-                ImGuiNET.ImGui.Separator();
-                foreach (ComboBoxItem<TItem> item in Items)
+                foreach (DropDownItem<TItem> item in Items)
                 {
                     if (!ImGuiNET.ImGui.Selectable(item.Name))
                         continue;
@@ -159,7 +172,7 @@ private unsafe int Propose(ImGuiInputTextCallbackData* data)
             int prevDiff = -1;
             string? itemName = null;
 
-            foreach (ComboBoxItem<TItem> item in Items)
+            foreach (DropDownItem<TItem> item in Items)
             {
                 if (!Identical(bufferString, item.Name, out int diff))
                     continue;
@@ -215,4 +228,19 @@ private bool Identical(string buf, string item, out int diff)
             return true;
         }
     }
+
+    public class DropDownItem<TItem>
+    {
+        public TItem Content { get; }
+
+        public LocalizedString Name { get; }
+
+        public DropDownItem(TItem content, LocalizedString name = default)
+        {
+            Content = content;
+            Name = name.IsEmpty ? (LocalizedString)content.ToString() : name;
+        }
+
+        public static implicit operator DropDownItem<TItem>(TItem o) => new(o);
+    }
 }
diff --git a/ImGui.Forms/Controls/DropDownBox.cs b/ImGui.Forms/Controls/DropDownBox.cs
deleted file mode 100644
index 072e1ed..0000000
--- a/ImGui.Forms/Controls/DropDownBox.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using ImGui.Forms.Controls.Base;
-using ImGui.Forms.Localization;
-using ImGui.Forms.Models;
-using ImGui.Forms.Resources;
-using Veldrid;
-
-namespace ImGui.Forms.Controls
-{
-    public class DropDownBox<TItem> : Component
-    {
-        #region Properties
-
-        public IList<ComboBoxItem<TItem>> Items { get; } = new List<ComboBoxItem<TItem>>();
-
-        public ComboBoxItem<TItem> SelectedItem { get; set; }
-
-        public SizeValue Width { get; set; } = SizeValue.Content;
-
-        #endregion
-
-        #region Events
-
-        public event EventHandler SelectedItemChanged;
-
-        #endregion
-
-        public override Size GetSize()
-        {
-            var maxWidth = Items.Select(x => TextMeasurer.GetCurrentLineWidth(x.Name)).DefaultIfEmpty(0).Max() + (int)ImGuiNET.ImGui.GetStyle().ItemInnerSpacing.X * 2;
-            var arrowWidth = 20;
-
-            SizeValue width = Width.IsContentAligned ? maxWidth + arrowWidth : Width;
-            var height = TextMeasurer.GetCurrentLineHeight() + (int)ImGuiNET.ImGui.GetStyle().ItemInnerSpacing.Y * 2;
-
-            return new Size(width, height);
-        }
-
-        protected override void UpdateInternal(Rectangle contentRect)
-        {
-            ImGuiNET.ImGui.SetNextItemWidth(contentRect.Width);
-
-            var selectedName = SelectedItem?.Name ?? string.Empty;
-            if (ImGuiNET.ImGui.BeginCombo($"##combo{Id}", selectedName))
-            {
-                for (var i = 0; i < Items.Count; i++)
-                {
-                    if (ImGuiNET.ImGui.Selectable(Items[i].Name, SelectedItem == Items[i]))
-                    {
-                        var hasChanged = SelectedItem != Items[i];
-                        SelectedItem = Items[i];
-
-                        if (hasChanged)
-                            OnSelectedItemChanged();
-                    }
-                }
-
-                ImGuiNET.ImGui.EndCombo();
-            }
-        }
-
-        private void OnSelectedItemChanged()
-        {
-            SelectedItemChanged?.Invoke(this, new EventArgs());
-        }
-    }
-
-    public class ComboBoxItem<TItem>
-    {
-        public TItem Content { get; }
-
-        public LocalizedString Name { get; }
-
-        public ComboBoxItem(TItem content, LocalizedString name = default)
-        {
-            Content = content;
-            Name = name.IsEmpty ? (LocalizedString)content.ToString() : name;
-        }
-
-        public static implicit operator ComboBoxItem<TItem>(TItem o) => new(o);
-    }
-}
diff --git a/ImGui.Forms/Controls/Expander.cs b/ImGui.Forms/Controls/Expander.cs
index 7ca91b9..9da0028 100644
--- a/ImGui.Forms/Controls/Expander.cs
+++ b/ImGui.Forms/Controls/Expander.cs
@@ -13,11 +13,10 @@ public class Expander : Component
         #region Properties
 
         public LocalizedString Caption { get; set; }
+        public Size Size { get; set; } = Size.WidthAlign;
 
         public Component Content { get; set; }
 
-        public int ContentHeight { get; set; } = 200;
-
         public bool Expanded { get; set; }
 
         #endregion
@@ -36,7 +35,13 @@ public Expander(Component content, LocalizedString caption = default)
 
         public override Size GetSize()
         {
-            return new Size(1f, (int)(GetHeaderHeight() + (Expanded ? ImGuiNET.ImGui.GetStyle().ItemSpacing.X + ContentHeight : 0)));
+            SizeValue height = Size.Height.IsContentAligned
+                    ? Expanded
+                        ? SizeValue.Absolute((int)(GetHeaderHeight() + ImGuiNET.ImGui.GetStyle().ItemSpacing.X + 200))
+                        : SizeValue.Absolute(GetHeaderHeight())
+                    : Size.Height;
+
+            return new Size(Size.Width, height);
         }
 
         protected override void UpdateInternal(Rectangle contentRect)
diff --git a/ImGui.Forms/Controls/ImageButton.cs b/ImGui.Forms/Controls/ImageButton.cs
index 36e9399..1bd596e 100644
--- a/ImGui.Forms/Controls/ImageButton.cs
+++ b/ImGui.Forms/Controls/ImageButton.cs
@@ -58,7 +58,7 @@ protected override void UpdateInternal(Rectangle contentRect)
             var enabled = Enabled;
             ApplyStyles(enabled);
 
-            if ((nint)Image != nint.Zero)
+            if (Image != null && (nint)Image != nint.Zero)
             {
                 if ((ImGuiNET.ImGui.ImageButton($"##{Id}", (nint)Image, GetImageSize()) || IsKeyDown(KeyAction)) && Enabled)
                     OnClicked();
diff --git a/ImGui.Forms/Controls/Label.cs b/ImGui.Forms/Controls/Label.cs
index c2f972f..405f123 100644
--- a/ImGui.Forms/Controls/Label.cs
+++ b/ImGui.Forms/Controls/Label.cs
@@ -18,7 +18,7 @@ public class Label : Component
 
         public FontResource Font { get; set; }
 
-        public float LineDistance { get; set; }
+        public int LineDistance { get; set; }
 
         public ThemedColor TextColor { get; set; }
 
diff --git a/ImGui.Forms/Controls/Layouts/StackLayout.cs b/ImGui.Forms/Controls/Layouts/StackLayout.cs
index b916ea0..6f9bbc8 100644
--- a/ImGui.Forms/Controls/Layouts/StackLayout.cs
+++ b/ImGui.Forms/Controls/Layouts/StackLayout.cs
@@ -73,14 +73,14 @@ public StackLayout()
 
         public override Size GetSize() => _size;
 
-        protected override int GetContentWidth(int parentWidth, float layoutCorrection = 1)
+        protected override int GetContentWidth(int parentWidth, int parentHeight, float layoutCorrection = 1)
         {
-            return _tableLayout.GetWidth(parentWidth, layoutCorrection);
+            return _tableLayout.GetWidth(parentWidth, parentHeight, layoutCorrection);
         }
 
-        protected override int GetContentHeight(int parentHeight, float layoutCorrection = 1)
+        protected override int GetContentHeight(int parentWidth, int parentHeight, float layoutCorrection = 1)
         {
-            return _tableLayout.GetHeight(parentHeight, layoutCorrection);
+            return _tableLayout.GetHeight(parentWidth, parentHeight, layoutCorrection);
         }
 
         protected override void UpdateInternal(Rectangle contentRect)
diff --git a/ImGui.Forms/Controls/Layouts/TableCell.cs b/ImGui.Forms/Controls/Layouts/TableCell.cs
index 9677c33..db8bcbb 100644
--- a/ImGui.Forms/Controls/Layouts/TableCell.cs
+++ b/ImGui.Forms/Controls/Layouts/TableCell.cs
@@ -47,18 +47,18 @@ public TableCell(Component component)
             Content = component;
         }
 
-        public int GetWidth(int parentWidth, float layoutCorrection = 1f)
+        public int GetWidth(int parentWidth, int parentHeight, float layoutCorrection = 1f)
         {
             if (Size.Width.IsContentAligned)
-                return Content?.GetWidth(parentWidth, layoutCorrection) ?? 0;
+                return Content?.GetWidth(parentWidth, parentHeight, layoutCorrection) ?? 0;
 
             return Component.GetDimension(Size.Width, parentWidth, layoutCorrection);
         }
 
-        public int GetHeight(int parentHeight, float layoutCorrection = 1f)
+        public int GetHeight(int parentWidth, int parentHeight, float layoutCorrection = 1f)
         {
             if (Size.Height.IsContentAligned)
-                return Content?.GetHeight(parentHeight, layoutCorrection) ?? 0;
+                return Content?.GetHeight(parentWidth, parentHeight, layoutCorrection) ?? 0;
 
             return Component.GetDimension(Size.Height, parentHeight, layoutCorrection);
         }
diff --git a/ImGui.Forms/Controls/Layouts/TableLayout.cs b/ImGui.Forms/Controls/Layouts/TableLayout.cs
index 7ee747e..499b5dd 100644
--- a/ImGui.Forms/Controls/Layouts/TableLayout.cs
+++ b/ImGui.Forms/Controls/Layouts/TableLayout.cs
@@ -36,15 +36,15 @@ public TableLayout()
 
         public override Size GetSize() => Size;
 
-        protected override int GetContentWidth(int parentWidth, float layoutCorrection = 1f)
+        protected override int GetContentWidth(int parentWidth, int parentHeight, float layoutCorrection = 1f)
         {
-            var widths = GetColumnWidths(parentWidth, layoutCorrection);
+            var widths = GetColumnWidths(parentWidth, parentHeight, layoutCorrection);
             return Math.Min(parentWidth, widths.Sum(x => x) + (widths.Length - 1) * (int)Spacing.X);
         }
 
-        protected override int GetContentHeight(int parentHeight, float layoutCorrection = 1)
+        protected override int GetContentHeight(int parentWidth, int parentHeight, float layoutCorrection = 1)
         {
-            var heights = GetRowHeights(parentHeight, layoutCorrection);
+            var heights = GetRowHeights(parentWidth, parentHeight, layoutCorrection);
             return Math.Min(parentHeight, heights.Sum(x => x) + (heights.Length - 1) * (int)Spacing.Y);
         }
 
@@ -75,8 +75,8 @@ public TableCell GetCell(int row, int col)
 
         protected override void UpdateInternal(Rectangle contentRect)
         {
-            var localWidths = GetColumnWidths(contentRect.Width, 1f);
-            var localHeights = GetRowHeights(contentRect.Height, 1f);
+            var localWidths = GetColumnWidths(contentRect.Width, contentRect.Height, 1f);
+            var localHeights = GetRowHeights(contentRect.Width, contentRect.Height, 1f);
 
             var totalWidth = localWidths.Sum() + Spacing.X * (localWidths.Length - 1);
             var totalHeight = localHeights.Sum() + Spacing.Y * (localHeights.Length - 1);
@@ -116,8 +116,8 @@ protected override void UpdateInternal(Rectangle contentRect)
 
                             // Apply cell alignment
                             var cellInternalSize = cell?.Content?.GetSize() ?? Size.Parent;
-                            var cellInternalWidth = cellInternalSize.Width.IsAbsolute ? cell?.Content?.GetWidth(cellWidth) ?? 0 : cellWidth;
-                            var cellInternalHeight = cellInternalSize.Height.IsAbsolute ? cell?.Content?.GetHeight(cellHeight) ?? 0 : cellHeight;
+                            var cellInternalWidth = cellInternalSize.Width.IsAbsolute ? cell?.Content?.GetWidth(cellWidth, cellHeight) ?? 0 : cellWidth;
+                            var cellInternalHeight = cellInternalSize.Height.IsAbsolute ? cell?.Content?.GetHeight(cellWidth, cellHeight) ?? 0 : cellHeight;
                             var cellXOffset = 0f;
                             var cellYOffset = 0f;
 
@@ -195,7 +195,7 @@ protected override void SetTabInactiveCore()
 
         #region Width calculation
 
-        private int[] GetColumnWidths(int componentWidth, float layoutCorrection)
+        private int[] GetColumnWidths(int componentWidth, int componentHeight, float layoutCorrection)
         {
             var maxColumnCount = GetMaxColumnCount();
             var result = Enumerable.Repeat(-1, maxColumnCount).ToArray();
@@ -217,7 +217,7 @@ private int[] GetColumnWidths(int componentWidth, float layoutCorrection)
                         var widthValue = (int)cell.Size.Width.Value;
 
                         var maxValue = widthValue < 0 ?
-                            cell.Content.GetWidth(componentWidth, layoutCorrection) :
+                            cell.Content.GetWidth(componentWidth, componentHeight, layoutCorrection) :
                             widthValue;
                         maxValue = Math.Min(availableWidth, maxValue);
 
@@ -256,7 +256,7 @@ private int[] GetColumnWidths(int componentWidth, float layoutCorrection)
                         maxIsAbsolute = true;
 
                         var maxValue = cellWidth.Value < 0 ?
-                            cell.GetWidth(componentWidth, layoutCorrection) :
+                            cell.GetWidth(componentWidth, componentHeight, layoutCorrection) :
                             (int)cellWidth.Value;
 
                         if (maxValue > maxCellWidth)
@@ -266,7 +266,7 @@ private int[] GetColumnWidths(int componentWidth, float layoutCorrection)
                     }
 
                     maxIsAbsolute = false;
-                    maxCellWidth = cell.GetWidth(availableWidth, widthCorrection);
+                    maxCellWidth = cell.GetWidth(availableWidth, componentHeight, widthCorrection);
                 }
 
                 // If max width is not absolute, do nothing
@@ -305,7 +305,7 @@ private float GetMaxRelativeWidth(int column)
 
         #region Height calculation
 
-        private int[] GetRowHeights(int componentHeight, float layoutCorrection)
+        private int[] GetRowHeights(int componentWidth, int componentHeight, float layoutCorrection)
         {
             var result = Enumerable.Repeat(-1, Rows.Count).ToArray();
 
@@ -324,7 +324,7 @@ private int[] GetRowHeights(int componentHeight, float layoutCorrection)
                         if (cell?.Content == null) continue;
 
                         var maxValue = cell.Size.Height.IsContentAligned ?
-                            cell.Content.GetHeight(componentHeight, layoutCorrection) :
+                            cell.Content.GetHeight(componentWidth, componentHeight, layoutCorrection) :
                             (int)cell.Size.Height.Value;
                         if (!Size.Height.IsContentAligned)
                             maxValue = Math.Min(availableHeight, maxValue);
@@ -365,7 +365,7 @@ private int[] GetRowHeights(int componentHeight, float layoutCorrection)
                         maxIsAbsolute = true;
 
                         var maxValue = cellHeight.IsContentAligned ?
-                            cell.GetHeight(componentHeight, layoutCorrection) :
+                            cell.GetHeight(componentWidth, componentHeight, layoutCorrection) :
                             cellHeight.Value;
                         if (!Size.Height.IsContentAligned)
                             maxValue = Math.Min(availableHeight, maxValue);
@@ -377,7 +377,7 @@ private int[] GetRowHeights(int componentHeight, float layoutCorrection)
                     }
 
                     maxIsAbsolute = false;
-                    maxCellHeight = cell.GetHeight(availableHeight, heightCorrection);
+                    maxCellHeight = cell.GetHeight(componentWidth, availableHeight, heightCorrection);
                 }
 
                 // If max height is not absolute, do nothing
diff --git a/ImGui.Forms/Controls/Layouts/ZLayout.cs b/ImGui.Forms/Controls/Layouts/ZLayout.cs
index aec9aea..545c045 100644
--- a/ImGui.Forms/Controls/Layouts/ZLayout.cs
+++ b/ImGui.Forms/Controls/Layouts/ZLayout.cs
@@ -36,8 +36,8 @@ protected override void UpdateInternal(Rectangle contentRect)
                     if (!(item?.Visible ?? false))
                         continue;
 
-                    var itemWidth = item.GetWidth(contentRect.Width);
-                    var itemHeight = item.GetHeight(contentRect.Height);
+                    var itemWidth = item.GetWidth(contentRect.Width, contentRect.Height);
+                    var itemHeight = item.GetHeight(contentRect.Width,contentRect.Height);
 
                     // Wrap positions
                     if (x + itemWidth + ItemSpacing.X >= contentRect.Width)
diff --git a/ImGui.Forms/Controls/Lists/ActivableList.cs b/ImGui.Forms/Controls/Lists/ActivableList.cs
index 1fc0f7b..10848d8 100644
--- a/ImGui.Forms/Controls/Lists/ActivableList.cs
+++ b/ImGui.Forms/Controls/Lists/ActivableList.cs
@@ -5,20 +5,21 @@
 
 namespace ImGui.Forms.Controls.Lists
 {
-    public class ActivableList : BaseList<ActivableComponent>
+    public class ActivableList<TItem> : List<TItem>
+        where TItem : ActivableComponent
     {
-        protected override void OnItemAdded(ItemEventArgs<ActivableComponent> e)
+        protected override void OnItemAdded(ItemEventArgs<TItem> e)
         {
             e.Item.Activated += Item_Activated;
         }
 
-        protected override void OnItemSet(ItemSetEventArgs<ActivableComponent> e)
+        protected override void OnItemSet(ItemSetEventArgs<TItem> e)
         {
             e.PreviousItem.Activated -= Item_Activated;
             e.Item.Activated += Item_Activated;
         }
 
-        protected override void OnItemRemoved(ItemEventArgs<ActivableComponent> e)
+        protected override void OnItemRemoved(ItemEventArgs<TItem> e)
         {
             e.Item.Activated -= Item_Activated;
         }
diff --git a/ImGui.Forms/Controls/Lists/BaseList.cs b/ImGui.Forms/Controls/Lists/BaseList.cs
deleted file mode 100644
index 8c57649..0000000
--- a/ImGui.Forms/Controls/Lists/BaseList.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using ImGui.Forms.Controls.Base;
-using ImGui.Forms.Controls.Layouts;
-using ImGuiNET;
-using Veldrid;
-using Size = ImGui.Forms.Models.Size;
-
-namespace ImGui.Forms.Controls.Lists
-{
-    public abstract class BaseList<TItem> : Component
-        where TItem : Component
-    {
-        private bool _scrollToLast;
-
-        public IList<TItem> Items { get; protected set; }
-        public Alignment Alignment { get; set; } = Alignment.Vertical;
-
-        public Size Size { get; set; } = Size.Content;
-        public int ItemSpacing { get; set; }
-        public Vector2 Padding { get; set; }
-        public bool ScrollToLastItem { get; set; }
-
-        public override Size GetSize() => Size;
-
-        public BaseList()
-        {
-            var items = new ObservableList<TItem>();
-
-            items.ItemInserted += Items_NewItem;
-            items.ItemAdded += Items_NewItem;
-            items.ItemSet += Items_ItemSet;
-            items.ItemRemoved += Items_ItemRemoved;
-
-            Items = items;
-        }
-
-        protected override int GetContentWidth(int parentWidth, float layoutCorrection = 1)
-        {
-            var widths = Items.Select(x => x.GetWidth(parentWidth, layoutCorrection)).DefaultIfEmpty(Size.Width.IsParentAligned ? parentWidth : 0).ToArray();
-
-            var totalWidth = Alignment == Alignment.Horizontal ?
-                widths.Sum() + Math.Max(0, Items.Count - 1) * ItemSpacing :
-                widths.Max();
-            return Math.Min(parentWidth, (int)(totalWidth + Padding.X * 2));
-        }
-
-        protected override int GetContentHeight(int parentHeight, float layoutCorrection = 1)
-        {
-            var heights = Items.Select(x => x.GetHeight(parentHeight, layoutCorrection)).DefaultIfEmpty(Size.Height.IsParentAligned ? parentHeight : 0).ToArray();
-
-            var totalHeight = Alignment == Alignment.Vertical ?
-                heights.Sum() + Math.Max(0, Items.Count - 1) * ItemSpacing :
-                heights.Max();
-            return Math.Min(parentHeight, (int)(totalHeight + Padding.Y * 2));
-        }
-
-        protected override void UpdateInternal(Rectangle contentRect)
-        {
-            var localItems = Items?.ToArray() ?? Array.Empty<Component>();
-
-            var listDimension = localItems.Sum(i => GetDimension(i, contentRect)) + Math.Max(0, localItems.Length - 1) * ItemSpacing + (int)(GetPadding() * 2);
-            var scrollableDimension = GetScrollableDimension(contentRect);
-
-            if (ImGuiNET.ImGui.BeginChild($"{Id}", contentRect.Size, ImGuiChildFlags.None))
-            {
-                if (_scrollToLast)
-                {
-                    SetScroll(listDimension - scrollableDimension);
-                    _scrollToLast = false;
-                }
-
-                var scrollbarDimension = listDimension > scrollableDimension ? (int)ImGuiNET.ImGui.GetStyle().ScrollbarSize : 0;
-                var (scrollX, scrollY) = (-(int)ImGuiNET.ImGui.GetScrollX(), -(int)ImGuiNET.ImGui.GetScrollY());
-
-                var (x, y) = (Padding.X, Padding.Y);
-
-                if (ImGuiNET.ImGui.BeginChild($"{Id}_in", GetInnerSize(listDimension, contentRect)))
-                {
-                    for (var i = 0; i < localItems.Length; i++)
-                    {
-                        var itemWidth = localItems[i].GetWidth(contentRect.Width - (Alignment == Alignment.Vertical ? scrollbarDimension : 0) - (int)(Padding.X * 2));
-                        var itemHeight = localItems[i].GetHeight(contentRect.Height - (Alignment == Alignment.Horizontal ? scrollbarDimension : 0) - (int)(Padding.Y * 2));
-
-                        ImGuiNET.ImGui.SetCursorPos(new Vector2(x, y));
-                        if (ImGuiNET.ImGui.BeginChild($"{Id}_itm{i}", new Vector2(itemWidth, itemHeight)))
-                            localItems[i].Update(new Rectangle((int)(contentRect.X + x + scrollX), (int)(contentRect.Y + y + scrollY), itemWidth, itemHeight));
-
-                        ImGuiNET.ImGui.EndChild();
-
-                        if (Alignment == Alignment.Vertical)
-                            y += itemHeight + ItemSpacing;
-                        else
-                            x += itemWidth + ItemSpacing;
-                    }
-                }
-
-                ImGuiNET.ImGui.EndChild();
-            }
-
-            ImGuiNET.ImGui.EndChild();
-        }
-
-        protected override void SetTabInactiveCore()
-        {
-            foreach (TItem item in Items)
-                item?.SetTabInactiveInternal();
-        }
-
-        #region Selection methods
-
-        private int GetDimension(Component component, Rectangle contentRect)
-        {
-            return Alignment == Alignment.Vertical
-                ? component.GetHeight(contentRect.Height)
-                : component.GetWidth(contentRect.Width);
-        }
-
-        private float GetPadding()
-        {
-            return Alignment == Alignment.Vertical
-                ? Padding.Y
-                : Padding.X;
-        }
-
-        private int GetScrollableDimension(Rectangle contentRect)
-        {
-            return Alignment == Alignment.Vertical
-                ? contentRect.Height
-                : contentRect.Width;
-        }
-
-        private Vector2 GetInnerSize(int listDimension, Rectangle contentRect)
-        {
-            return Alignment == Alignment.Vertical ?
-                new Vector2(contentRect.Width, listDimension) :
-                new Vector2(listDimension, contentRect.Height);
-        }
-
-        private void SetScroll(float scroll)
-        {
-            scroll = Math.Max(0, scroll);
-
-            if (Alignment == Alignment.Vertical)
-                ImGuiNET.ImGui.SetScrollY(scroll);
-            else
-                ImGuiNET.ImGui.SetScrollX(scroll);
-        }
-
-        #endregion
-
-        #region Observable events
-
-        private void Items_NewItem(object sender, ItemEventArgs<TItem> e)
-        {
-            OnItemAdded(e);
-        }
-
-        private void Items_ItemSet(object sender, ItemSetEventArgs<TItem> e)
-        {
-            OnItemSet(e);
-        }
-
-        private void Items_ItemRemoved(object sender, ItemEventArgs<TItem> e)
-        {
-            OnItemRemoved(e);
-        }
-
-        protected virtual void OnItemAdded(ItemEventArgs<TItem> e)
-        {
-            _scrollToLast = ScrollToLastItem;
-        }
-
-        protected virtual void OnItemSet(ItemSetEventArgs<TItem> e) { }
-
-        protected virtual void OnItemRemoved(ItemEventArgs<TItem> e) { }
-
-        #endregion
-    }
-}
diff --git a/ImGui.Forms/Controls/Lists/DataTable.cs b/ImGui.Forms/Controls/Lists/DataTable.cs
index 3b725b7..8b9806b 100644
--- a/ImGui.Forms/Controls/Lists/DataTable.cs
+++ b/ImGui.Forms/Controls/Lists/DataTable.cs
@@ -1,10 +1,8 @@
 using System;
 using System.Collections.Generic;
-using System.Drawing;
 using System.Linq;
 using ImGui.Forms.Controls.Base;
 using ImGui.Forms.Controls.Menu;
-using ImGui.Forms.Extensions;
 using ImGuiNET;
 using Rectangle = Veldrid.Rectangle;
 using Size = ImGui.Forms.Models.Size;
@@ -14,13 +12,13 @@ namespace ImGui.Forms.Controls.Lists
     public class DataTable<TData> : Component
     {
         private (int, int) _clickedCell = (-1, -1);
-        private readonly IList<int> _selectedIndexes = new List<int>();
+        private readonly IList<int> _selectedIndexes = new System.Collections.Generic.List<int>();
 
-        private IList<DataTableRow<TData>> _rows = new List<DataTableRow<TData>>();
+        private IList<DataTableRow<TData>> _rows = new System.Collections.Generic.List<DataTableRow<TData>>();
 
         #region Properties
 
-        public IList<DataTableColumn<TData>> Columns { get; } = new List<DataTableColumn<TData>>();
+        public IList<DataTableColumn<TData>> Columns { get; } = new System.Collections.Generic.List<DataTableColumn<TData>>();
 
         public IList<DataTableRow<TData>> Rows
         {
@@ -57,19 +55,16 @@ public IList<DataTableRow<TData>> Rows
 
         #endregion
 
-        public override Size GetSize()
-        {
-            return Size;
-        }
+        public override Size GetSize() => Size;
 
         protected override void UpdateInternal(Rectangle contentRect)
         {
-            var localRows = _rows ?? new List<DataTableRow<TData>>();
+            var localRows = _rows ?? new System.Collections.Generic.List<DataTableRow<TData>>();
 
             var flags = ImGuiTableFlags.BordersV;
             if (IsResizable) flags |= ImGuiTableFlags.Resizable;
 
-            if (ImGuiNET.ImGui.BeginChild($"{Id}c"))
+            if (ImGuiNET.ImGui.BeginChild($"{Id}c", contentRect.Size))
             {
                 if (ImGuiNET.ImGui.BeginTable($"{Id}t", Columns.Count, flags))
                 {
diff --git a/ImGui.Forms/Controls/Lists/ImageList.cs b/ImGui.Forms/Controls/Lists/ImageList.cs
deleted file mode 100644
index 68ce421..0000000
--- a/ImGui.Forms/Controls/Lists/ImageList.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using ImGui.Forms.Controls.Base;
-using ImGui.Forms.Controls.Layouts;
-using ImGui.Forms.Extensions;
-using ImGui.Forms.Factories;
-using ImGui.Forms.Models;
-using ImGui.Forms.Resources;
-using ImGui.Forms.Support;
-using ImGuiNET;
-using Veldrid;
-
-namespace ImGui.Forms.Controls.Lists
-{
-    public class ImageList : Component
-    {
-        // HINT: ImGuiCol.Header is used for selected items
-        // HINT: ImGuiCol.HeaderHovered is used for hovered items
-        // HINT: ImGuiCol.HeaderActive is used for hovered and clicked items
-
-        public IList<ImageListItem> Items { get; }
-        public ImageListItem SelectedItem { get; set; }
-
-        public Vector2 ThumbnailSize { get; set; } = new(30, 30);
-        public bool ShowThumbnailBorder { get; set; }
-
-        public FontResource Font { get; set; }
-
-        public Vector2 Padding { get; set; }
-        public int ItemPadding { get; set; }
-
-        public Size Size { get; set; } = Size.Parent;
-
-        #region Events
-
-        public event EventHandler SelectedItemChanged;
-
-        #endregion
-
-        public ImageList()
-        {
-            var items = new ObservableList<ImageListItem>();
-            items.ItemAdded += Items_ItemAdded;
-            items.ItemRemoved += Items_ItemRemoved;
-            items.ItemSet += Items_ItemSet;
-            items.ItemInserted += Items_ItemInserted;
-
-            Items = items;
-        }
-
-        public override Size GetSize() => Size;
-
-        protected override void UpdateInternal(Rectangle contentRect)
-        {
-            ImageListItem selectedItem = null;
-
-            if (ImGuiNET.ImGui.BeginChild($"##{Id}_out", new Vector2(contentRect.Width, contentRect.Height), ImGuiChildFlags.None))
-            {
-                var textHeight = TextMeasurer.GetCurrentLineHeight(Font);
-
-                var itemHeight = Math.Max((int)ThumbnailSize.Y, textHeight);
-                var itemDimensions = new Vector2(contentRect.Width - Padding.X * 2, itemHeight);
-                var contentPos = new Vector2(contentRect.X + Padding.X, contentRect.Y + Padding.Y);
-                var scrollY = ImGuiNET.ImGui.GetScrollY();
-
-                var localItems = Items.ToArray();
-
-                if (ImGuiNET.ImGui.BeginChild($"##{Id}_in", new Vector2(contentRect.Width, Padding.Y * 2 + localItems.Length * (itemHeight + ItemPadding)), ImGuiChildFlags.None, ImGuiWindowFlags.NoScrollbar))
-                {
-                    for (var i = 0; i < localItems.Length; i++)
-                    {
-                        var item = localItems[i];
-                        var itemId = IdFactory.Get(item);
-
-                        var itemPos = new Vector2(Padding.X, i * itemHeight + Padding.Y + i * ItemPadding);
-                        var contentItemPos = contentPos + itemPos;
-                        var contentScrollPos = contentItemPos - new Vector2(0, scrollY);
-                        var contentScrollEndPos = contentScrollPos + itemDimensions;
-
-                        // Create dummy control for states
-                        ImGuiNET.ImGui.PushID(itemId);
-                        ImGuiSupport.Dummy(itemId, itemPos - new Vector2(0, scrollY), itemDimensions);
-
-                        // Create item states
-                        var isItemSelected = item == SelectedItem;
-                        var isItemHovered = ImGuiNET.ImGui.IsItemHovered() &&
-                                            (int)(ImGuiNET.ImGui.GetMousePos().Y - contentPos.Y - Padding.Y + scrollY) /
-                                            (itemHeight + ItemPadding) == i;
-                        var isItemClicked = isItemHovered && ImGuiNET.ImGui.IsMouseDown(ImGuiMouseButton.Left);
-
-                        ImGuiNET.ImGui.PopID();
-
-                        // Set selected item locally
-                        if (isItemHovered && ImGuiNET.ImGui.IsMouseClicked(ImGuiMouseButton.Left))
-                            selectedItem = item;
-
-                        // Add background color for selection
-                        var color = isItemClicked ? ImGuiNET.ImGui.GetColorU32(ImGuiCol.HeaderActive) :
-                        isItemHovered ? ImGuiNET.ImGui.GetColorU32(ImGuiCol.HeaderHovered) :
-                        isItemSelected ? ImGuiNET.ImGui.GetColorU32(ImGuiCol.Header) : 0;
-
-                        if (isItemHovered || isItemSelected)
-                            ImGuiNET.ImGui.GetWindowDrawList().AddRectFilled(contentScrollPos, contentScrollEndPos, color);
-
-                        // Add thumbnail
-                        if (item.Image != null && ThumbnailSize.X != 0 && ThumbnailSize.Y != 0)
-                        {
-                            var imgPos = contentScrollPos;
-                            var thumbnailRect = new Vector2(ThumbnailSize.X, ThumbnailSize.Y);
-
-                            if (item.RetainAspectRatio)
-                            {
-                                var heightSmaller = item.Image.Height < item.Image.Width;
-                                var ratio = heightSmaller ?
-                                    (float)item.Image.Height / item.Image.Width :
-                                    (float)item.Image.Width / item.Image.Height;
-
-                                var retainedWidth = !heightSmaller ? ThumbnailSize.Y * ratio : ThumbnailSize.X;
-                                var retainedHeight = heightSmaller ? ThumbnailSize.X * ratio : ThumbnailSize.Y;
-
-                                imgPos = heightSmaller ?
-                                    new Vector2(imgPos.X, imgPos.Y + (ThumbnailSize.Y - retainedHeight) / 2f) :
-                                    new Vector2(imgPos.X + (ThumbnailSize.X - retainedWidth) / 2f, imgPos.Y);
-                                thumbnailRect = new Vector2(retainedWidth, retainedHeight);
-                            }
-
-                            ImGuiNET.ImGui.GetWindowDrawList().AddImage((nint)item.Image, imgPos, imgPos + thumbnailRect);
-
-                            if (ShowThumbnailBorder)
-                                ImGuiNET.ImGui.GetWindowDrawList().AddRect(contentScrollPos, contentScrollPos + new Vector2(ThumbnailSize.X, ThumbnailSize.Y), Style.GetColor(ImGuiCol.Border).ToUInt32(), 0);
-                        }
-
-                        // Add text
-                        var textPos = contentScrollPos + new Vector2(ThumbnailSize.X + 2, (itemHeight - textHeight) / 2f);
-
-                        ImFontPtr? fontPtr = Font?.GetPointer();
-                        if (fontPtr != null)
-                            ImGuiNET.ImGui.GetWindowDrawList().AddText(fontPtr.Value, Font.Data.Size, textPos, 0xFFFFFFFF, item.Text);
-                        else
-                            ImGuiNET.ImGui.GetWindowDrawList().AddText(textPos, 0xFFFFFFFF, item.Text);
-                    }
-                }
-
-                ImGuiNET.ImGui.EndChild();
-            }
-
-            ImGuiNET.ImGui.EndChild();
-
-            // Invoke selected item change event
-            if (selectedItem != null && SelectedItem != selectedItem)
-            {
-                SelectedItem = selectedItem;
-                OnSelectedItemChanged();
-            }
-        }
-
-        #region Event Invokers
-
-        private void OnSelectedItemChanged()
-        {
-            SelectedItemChanged?.Invoke(this, new EventArgs());
-        }
-
-        #endregion
-
-        #region Event Methods
-
-        private void Items_ItemAdded(object sender, ItemEventArgs<ImageListItem> e)
-        {
-        }
-
-        private void Items_ItemRemoved(object sender, ItemEventArgs<ImageListItem> e)
-        {
-        }
-
-        private void Items_ItemInserted(object sender, ItemEventArgs<ImageListItem> e)
-        {
-        }
-
-        private void Items_ItemSet(object sender, ItemEventArgs<ImageListItem> e)
-        {
-        }
-
-        #endregion
-    }
-}
diff --git a/ImGui.Forms/Controls/Lists/ImageListItem.cs b/ImGui.Forms/Controls/Lists/ImageListItem.cs
deleted file mode 100644
index 694c112..0000000
--- a/ImGui.Forms/Controls/Lists/ImageListItem.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using ImGui.Forms.Localization;
-using ImGui.Forms.Resources;
-
-namespace ImGui.Forms.Controls.Lists
-{
-    public class ImageListItem
-    {
-        private ThemedImageResource _baseImg;
-
-        public ThemedImageResource Image
-        {
-            get => _baseImg;
-            set
-            {
-                _baseImg?.Destroy();
-                _baseImg = value;
-            }
-        }
-
-        public bool RetainAspectRatio { get; set; } = true;
-
-        public LocalizedString Text { get; set; }
-    }
-}
diff --git a/ImGui.Forms/Controls/Lists/List.cs b/ImGui.Forms/Controls/Lists/List.cs
index a070ad2..58ed5c1 100644
--- a/ImGui.Forms/Controls/Lists/List.cs
+++ b/ImGui.Forms/Controls/Lists/List.cs
@@ -1,8 +1,280 @@
-using ImGui.Forms.Controls.Base;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using ImGui.Forms.Controls.Base;
+using ImGui.Forms.Controls.Layouts;
+using ImGuiNET;
+using Veldrid;
+using Size = ImGui.Forms.Models.Size;
 
 namespace ImGui.Forms.Controls.Lists
 {
-    public class List : BaseList<Component>
+    public class List<TItem> : Component where TItem : Component
     {
+        private bool _scrollToLast;
+
+        #region Properties
+
+        public IList<TItem> Items { get; protected set; }
+        public TItem SelectedItem { get; set; }
+
+        public Alignment Alignment { get; set; } = Alignment.Vertical;
+
+        public Size Size { get; set; } = Size.Content;
+
+        public int ItemSpacing { get; set; }
+        public Vector2 Padding { get; set; }
+
+        public bool ScrollToLastItem { get; set; }
+
+        public bool IsSelectable { get; set; } = false;
+
+        #endregion
+
+        #region Events
+
+        public event EventHandler SelectedItemChanged;
+
+        #endregion
+
+        public List()
+        {
+            var items = new ObservableList<TItem>();
+
+            items.ItemInserted += Items_NewItem;
+            items.ItemAdded += Items_NewItem;
+            items.ItemSet += Items_ItemSet;
+            items.ItemRemoved += Items_ItemRemoved;
+
+            Items = items;
+        }
+
+        public override Size GetSize() => Size;
+
+        protected override void UpdateInternal(Rectangle contentRect)
+        {
+            TItem selectedItem = null;
+            var localItems = Items?.ToArray() ?? Array.Empty<TItem>();
+
+            var listDimension = localItems.Sum(i => GetDimension(i, contentRect)) + Math.Max(0, localItems.Length - 1) * ItemSpacing + (int)(GetPadding() * 2);
+            var scrollableDimension = GetScrollableDimension(contentRect);
+
+            var flags = ImGuiWindowFlags.None;
+            if (Alignment == Alignment.Horizontal)
+                flags |= ImGuiWindowFlags.HorizontalScrollbar;
+
+            if (ImGuiNET.ImGui.BeginChild($"{Id}", contentRect.Size, ImGuiChildFlags.None, flags))
+            {
+                if (_scrollToLast)
+                {
+                    SetScroll(listDimension - scrollableDimension);
+                    _scrollToLast = false;
+                }
+
+                var scrollbarDimension = listDimension > scrollableDimension ? (int)ImGuiNET.ImGui.GetStyle().ScrollbarSize : 0;
+                var scroll = new Vector2(-(int)ImGuiNET.ImGui.GetScrollX(), -(int)ImGuiNET.ImGui.GetScrollY());
+
+                var (x, y) = (Padding.X, Padding.Y);
+                var contentPos = new Vector2(contentRect.X + x, contentRect.Y + y);
+
+                if (ImGuiNET.ImGui.BeginChild($"{Id}_in", GetInnerSize(listDimension, contentRect), ImGuiChildFlags.None))
+                {
+                    for (var i = 0; i < localItems.Length; i++)
+                    {
+                        var item = localItems[i];
+
+                        var itemWidth = item.GetWidth(contentRect.Width - (Alignment == Alignment.Vertical ? scrollbarDimension : 0) - (int)(Padding.X * 2), contentRect.Height);
+                        var itemHeight = item.GetHeight(contentRect.Width, contentRect.Height - (Alignment == Alignment.Horizontal ? scrollbarDimension : 0) - (int)(Padding.Y * 2));
+                        var itemSize = new Vector2(itemWidth, itemHeight);
+
+                        var contentItemPos = new Vector2(contentRect.X + x, contentRect.Y + y);
+                        var contentItemScrollPos = contentItemPos + scroll;
+                        var contentItemScrollEndPos = contentItemScrollPos + itemSize;
+
+                        var contentScrollPos = contentPos + scroll;
+
+                        // Create item states
+                        var isItemSelected = item == SelectedItem;
+                        var isItemHovered = ImGuiNET.ImGui.IsMouseHoveringRect(contentRect.Position, contentRect.Position + contentRect.Size)
+                                            && (Alignment == Alignment.Vertical
+                            ? (int)(ImGuiNET.ImGui.GetMousePos().Y - contentScrollPos.Y) / (itemHeight + ItemSpacing) == i
+                            : (int)(ImGuiNET.ImGui.GetMousePos().X - contentScrollPos.X) / (itemWidth + ItemSpacing) == i);
+                        var isItemClicked = isItemHovered && ImGuiNET.ImGui.IsMouseDown(ImGuiMouseButton.Left);
+
+                        // Set selected item locally
+                        if (IsSelectable && isItemHovered && ImGuiNET.ImGui.IsMouseClicked(ImGuiMouseButton.Left))
+                            selectedItem = item;
+
+                        // Add background color for selection
+                        var color = isItemClicked
+                            ? ImGuiNET.ImGui.GetColorU32(ImGuiCol.HeaderActive)
+                            : isItemHovered
+                                ? ImGuiNET.ImGui.GetColorU32(ImGuiCol.HeaderHovered)
+                                : isItemSelected
+                                    ? ImGuiNET.ImGui.GetColorU32(ImGuiCol.Header)
+                                    : 0;
+
+                        if (IsSelectable && (isItemHovered || isItemSelected))
+                            ImGuiNET.ImGui.GetWindowDrawList().AddRectFilled(contentItemScrollPos, contentItemScrollEndPos, color);
+
+                        ImGuiNET.ImGui.SetCursorPos(new Vector2(x, y));
+                        if (ImGuiNET.ImGui.BeginChild($"{Id}_itm{i}", itemSize))
+                            item.Update(new Rectangle((int)contentItemScrollPos.X, (int)contentItemScrollPos.Y, itemWidth, itemHeight));
+
+                        ImGuiNET.ImGui.EndChild();
+
+                        if (Alignment == Alignment.Vertical)
+                            y += itemHeight + ItemSpacing;
+                        else
+                            x += itemWidth + ItemSpacing;
+                    }
+                }
+
+                ImGuiNET.ImGui.EndChild();
+            }
+
+            ImGuiNET.ImGui.EndChild();
+
+            // Invoke selected item change event
+            if (IsSelectable && selectedItem != null && SelectedItem != selectedItem)
+            {
+                SelectedItem = selectedItem;
+                OnSelectedItemChanged();
+            }
+        }
+
+        protected override int GetContentWidth(int parentWidth, int parentHeight, float layoutCorrection = 1f)
+        {
+            // If no items, return 0
+            if (Items.Count <= 0)
+                return 0;
+
+            int totalWidth = GetTotalWidth(parentWidth, parentHeight, layoutCorrection);
+            int totalHeight = GetTotalHeight(parentWidth, parentHeight, layoutCorrection);
+
+            // Allocate for scroll bar
+            if (Alignment == Alignment.Vertical && totalHeight > GetHeight(parentWidth, parentHeight, layoutCorrection))
+                totalWidth += (int)ImGuiNET.ImGui.GetStyle().ScrollbarSize;
+
+            return Math.Min(parentWidth, (int)(totalWidth + Padding.X * 2));
+        }
+
+        private int GetTotalWidth(int parentWidth, int parentHeight, float layoutCorrection)
+        {
+            int[] widths = Items.Select(x => x.GetWidth(parentWidth, parentHeight, layoutCorrection)).ToArray();
+
+            // Base on alignment, sum widths or take maximum
+            return Alignment == Alignment.Horizontal ?
+                widths.Sum() + (Items.Count - 1) * ItemSpacing :
+                widths.Max();
+        }
+
+        protected override int GetContentHeight(int parentWidth, int parentHeight, float layoutCorrection = 1f)
+        {
+            // If no items, return 0
+            if (Items.Count <= 0)
+                return 0;
+
+            int totalWidth = GetTotalWidth(parentWidth, parentHeight, layoutCorrection);
+            int totalHeight = GetTotalHeight(parentWidth, parentHeight, layoutCorrection);
+
+            // Allocate for scroll bar
+            if (Alignment == Alignment.Horizontal && totalWidth > GetWidth(parentWidth, parentHeight, layoutCorrection))
+                totalHeight += (int)ImGuiNET.ImGui.GetStyle().ScrollbarSize;
+
+            return Math.Min(parentHeight, (int)(totalHeight + Padding.X * 2));
+        }
+
+        private int GetTotalHeight(int parentWidth, int parentHeight, float layoutCorrection)
+        {
+            int[] heights = Items.Select(x => x.GetHeight(parentWidth, parentHeight, layoutCorrection)).ToArray();
+
+            // Base on alignment, sum heights or take maximum
+            return Alignment == Alignment.Vertical ?
+                heights.Sum() + (Items.Count - 1) * ItemSpacing :
+                heights.Max();
+        }
+
+        protected override void SetTabInactiveCore()
+        {
+            foreach (TItem item in Items)
+                item?.SetTabInactiveInternal();
+        }
+
+        private void OnSelectedItemChanged()
+        {
+            SelectedItemChanged?.Invoke(this, EventArgs.Empty);
+        }
+
+        #region Selection methods
+
+        private int GetDimension(Component component, Rectangle contentRect)
+        {
+            return Alignment == Alignment.Vertical
+                ? component.GetHeight(contentRect.Width, contentRect.Height)
+                : component.GetWidth(contentRect.Width, contentRect.Height);
+        }
+
+        private float GetPadding()
+        {
+            return Alignment == Alignment.Vertical
+                ? Padding.Y
+                : Padding.X;
+        }
+
+        private int GetScrollableDimension(Rectangle contentRect)
+        {
+            return Alignment == Alignment.Vertical
+                ? contentRect.Height
+                : contentRect.Width;
+        }
+
+        private Vector2 GetInnerSize(int listDimension, Rectangle contentRect)
+        {
+            return Alignment == Alignment.Vertical ?
+                new Vector2(contentRect.Width, listDimension) :
+                new Vector2(listDimension, contentRect.Height);
+        }
+
+        private void SetScroll(float scroll)
+        {
+            scroll = Math.Max(0, scroll);
+
+            if (Alignment == Alignment.Vertical)
+                ImGuiNET.ImGui.SetScrollY(scroll);
+            else
+                ImGuiNET.ImGui.SetScrollX(scroll);
+        }
+
+        #endregion
+
+        #region Observable events
+
+        private void Items_NewItem(object sender, ItemEventArgs<TItem> e)
+        {
+            OnItemAdded(e);
+        }
+
+        private void Items_ItemSet(object sender, ItemSetEventArgs<TItem> e)
+        {
+            OnItemSet(e);
+        }
+
+        private void Items_ItemRemoved(object sender, ItemEventArgs<TItem> e)
+        {
+            OnItemRemoved(e);
+        }
+
+        protected virtual void OnItemAdded(ItemEventArgs<TItem> e)
+        {
+            _scrollToLast = ScrollToLastItem;
+        }
+
+        protected virtual void OnItemSet(ItemSetEventArgs<TItem> e) { }
+
+        protected virtual void OnItemRemoved(ItemEventArgs<TItem> e) { }
+
+        #endregion
     }
 }
diff --git a/ImGui.Forms/Controls/Menu/MenuBar.cs b/ImGui.Forms/Controls/Menu/MenuBar.cs
index e980fe1..70fb53b 100644
--- a/ImGui.Forms/Controls/Menu/MenuBar.cs
+++ b/ImGui.Forms/Controls/Menu/MenuBar.cs
@@ -5,7 +5,7 @@
 
 namespace ImGui.Forms.Controls.Menu
 {
-    public class MenuBar
+    public abstract class MenuBar
     {
         private readonly bool _isMain;
 
@@ -15,10 +15,6 @@ public class MenuBar
 
         public int Height => GetHeight();
 
-        protected MenuBar() : this(false)
-        {
-        }
-
         protected MenuBar(bool isMain)
         {
             _isMain = isMain;
@@ -31,8 +27,8 @@ public void Update()
                 ImGuiNET.ImGui.PushFont(fontPtr.Value);
 
             // Begin menu bar
-            bool isMenuOpen = _isMain ? 
-                ImGuiNET.ImGui.BeginMainMenuBar() : 
+            bool isMenuOpen = _isMain ?
+                ImGuiNET.ImGui.BeginMainMenuBar() :
                 ImGuiNET.ImGui.BeginMenuBar();
             if (isMenuOpen)
             {
@@ -48,7 +44,7 @@ public void Update()
                 foreach (var child in Items)
                     child.UpdateEvents();
             }
-            
+
             if (fontPtr != null)
                 ImGuiNET.ImGui.PopFont();
         }
@@ -59,9 +55,11 @@ private int GetHeight()
             if (fontPtr != null)
                 ImGuiNET.ImGui.PushFont(fontPtr.Value);
 
-            // HINT: It's currently unknown where those 3 pixels come from, but they have to be added to get the correct size of the menu
-            var height = Items.Max(x => x.Height) + 3;
-            
+            var height = Items.Count > 0
+                ? Items.Max(x => x.Height) :
+                // HINT: It's currently unknown where those 3 pixels come from, but they have to be added to get the correct size of the menu
+                (int)(TextMeasurer.GetCurrentLineHeight() + ImGuiNET.ImGui.GetStyle().FramePadding.Y * 2) + 3;
+
             if (fontPtr != null)
                 ImGuiNET.ImGui.PopFont();
 
diff --git a/ImGui.Forms/Controls/Menu/MenuBarButton.cs b/ImGui.Forms/Controls/Menu/MenuBarButton.cs
index 038efd9..a41efa2 100644
--- a/ImGui.Forms/Controls/Menu/MenuBarButton.cs
+++ b/ImGui.Forms/Controls/Menu/MenuBarButton.cs
@@ -2,6 +2,7 @@
 using ImGui.Forms.Localization;
 using ImGui.Forms.Models.IO;
 using ImGui.Forms.Resources;
+using ImGuiNET;
 
 namespace ImGui.Forms.Controls.Menu
 {
@@ -11,6 +12,8 @@ public class MenuBarButton : MenuBarItem
 
         public LocalizedString Text { get; set; }
 
+        public FontResource Font { get; set; }
+
         public KeyCommand KeyAction { get; set; }
 
         public override int Height => GetHeight();
@@ -23,7 +26,7 @@ public class MenuBarButton : MenuBarItem
 
         #endregion
 
-        public MenuBarButton(LocalizedString text)
+        public MenuBarButton(LocalizedString text = default)
         {
             Text = text;
         }
@@ -31,7 +34,7 @@ public MenuBarButton(LocalizedString text)
         protected override void UpdateInternal()
         {
             // Add menu button
-            if (ImGuiNET.ImGui.MenuItem(Text, Enabled) || IsKeyDown(KeyAction))
+            if ((ImGuiNET.ImGui.MenuItem(Text, Enabled) || IsKeyDown(KeyAction)) && Enabled)
                 // Execute click event, if set
                 Clicked?.Invoke(this, EventArgs.Empty);
         }
@@ -39,7 +42,7 @@ protected override void UpdateInternal()
         protected override void UpdateEventsInternal()
         {
             // Add menu button
-            if (IsKeyDown(KeyAction))
+            if (IsKeyDown(KeyAction) && Enabled)
                 // Execute click event, if set
                 Clicked?.Invoke(this, EventArgs.Empty);
         }
@@ -49,11 +52,24 @@ private int GetHeight()
             ApplyStyles();
 
             var textSize = TextMeasurer.MeasureText(Text);
-            var height = (int)(textSize.Y + ImGuiNET.ImGui.GetStyle().FramePadding.Y);
+            var height = (int)(textSize.Y + ImGuiNET.ImGui.GetStyle().FramePadding.Y * 2);
 
             RemoveStyles();
 
             return height;
         }
+
+        protected override void ApplyStyles()
+        {
+            ImFontPtr? fontPtr = Font?.GetPointer();
+            if (fontPtr != null)
+                ImGuiNET.ImGui.PushFont(fontPtr.Value);
+        }
+
+        protected override void RemoveStyles()
+        {
+            if (Font?.GetPointer() != null)
+                ImGuiNET.ImGui.PopFont();
+        }
     }
 }
diff --git a/ImGui.Forms/Controls/Menu/MenuBarCheckBox.cs b/ImGui.Forms/Controls/Menu/MenuBarCheckBox.cs
index 13bd366..f874835 100644
--- a/ImGui.Forms/Controls/Menu/MenuBarCheckBox.cs
+++ b/ImGui.Forms/Controls/Menu/MenuBarCheckBox.cs
@@ -1,6 +1,7 @@
 using System;
 using ImGui.Forms.Localization;
 using ImGui.Forms.Resources;
+using ImGuiNET;
 
 namespace ImGui.Forms.Controls.Menu
 {
@@ -12,6 +13,8 @@ public class MenuBarCheckBox : MenuBarItem
 
         public LocalizedString Text { get; set; }
 
+        public FontResource Font { get; set; }
+
         public override int Height => GetHeight();
 
         public bool Checked
@@ -32,7 +35,7 @@ public bool Checked
 
         #endregion
 
-        public MenuBarCheckBox(LocalizedString text)
+        public MenuBarCheckBox(LocalizedString text = default)
         {
             Text = text;
         }
@@ -54,16 +57,29 @@ private int GetHeight()
             ApplyStyles();
 
             var textSize = TextMeasurer.MeasureText(Text);
-            var height = (int)(textSize.Y + ImGuiNET.ImGui.GetStyle().FramePadding.Y);
+            var height = (int)(textSize.Y + ImGuiNET.ImGui.GetStyle().FramePadding.Y * 2);
 
             RemoveStyles();
 
             return height;
         }
 
+        protected override void ApplyStyles()
+        {
+            ImFontPtr? fontPtr = Font?.GetPointer();
+            if (fontPtr != null)
+                ImGuiNET.ImGui.PushFont(fontPtr.Value);
+        }
+
+        protected override void RemoveStyles()
+        {
+            if (Font?.GetPointer() != null)
+                ImGuiNET.ImGui.PopFont();
+        }
+
         private void OnCheckedChanged()
         {
-            CheckChanged?.Invoke(this, new EventArgs());
+            CheckChanged?.Invoke(this, EventArgs.Empty);
         }
     }
 }
diff --git a/ImGui.Forms/Controls/Menu/MenuBarMenu.cs b/ImGui.Forms/Controls/Menu/MenuBarMenu.cs
index a0361ad..1e1532b 100644
--- a/ImGui.Forms/Controls/Menu/MenuBarMenu.cs
+++ b/ImGui.Forms/Controls/Menu/MenuBarMenu.cs
@@ -12,6 +12,8 @@ public class MenuBarMenu : MenuBarItem
 
         public LocalizedString Text { get; set; }
 
+        public FontResource Font { get; set; }
+
         public Vector2 Padding { get; set; } = new(8, 8);
 
         public IList<MenuBarItem> Items { get; } = new List<MenuBarItem>();
@@ -20,16 +22,13 @@ public class MenuBarMenu : MenuBarItem
 
         #endregion
 
-        public MenuBarMenu(LocalizedString text)
+        public MenuBarMenu(LocalizedString text = default)
         {
             Text = text;
         }
 
         protected override void UpdateInternal()
         {
-            ImGuiNET.ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(4, 5));
-            ImGuiNET.ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Padding);
-
             if (ImGuiNET.ImGui.BeginMenu(Text, Enabled))
             {
                 foreach (var item in Items)
@@ -39,13 +38,13 @@ protected override void UpdateInternal()
             }
             else
                 UpdateEventsInternal();
-
-
-            ImGuiNET.ImGui.PopStyleVar(2);
         }
 
         protected override void UpdateEventsInternal()
         {
+            if (!Enabled)
+                return;
+
             foreach (var item in Items)
                 item.UpdateEvents();
         }
@@ -55,11 +54,29 @@ private int GetHeight()
             ApplyStyles();
 
             var textSize = TextMeasurer.MeasureText(Text);
-            var height = (int)(textSize.Y + ImGuiNET.ImGui.GetStyle().FramePadding.Y);
+            var height = (int)(textSize.Y + ImGuiNET.ImGui.GetStyle().FramePadding.Y * 2);
 
             RemoveStyles();
 
             return height;
         }
+
+        protected override void ApplyStyles()
+        {
+            ImGuiNET.ImGui.PushStyleVar(ImGuiStyleVar.WindowPadding, new Vector2(4, 5));
+            ImGuiNET.ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Padding);
+
+            ImFontPtr? fontPtr = Font?.GetPointer();
+            if (fontPtr != null)
+                ImGuiNET.ImGui.PushFont(fontPtr.Value);
+        }
+
+        protected override void RemoveStyles()
+        {
+            if (Font?.GetPointer() != null)
+                ImGuiNET.ImGui.PopFont();
+
+            ImGuiNET.ImGui.PopStyleVar(2);
+        }
     }
 }
diff --git a/ImGui.Forms/Controls/Menu/MenuBarRadio.cs b/ImGui.Forms/Controls/Menu/MenuBarRadio.cs
index fbb8a62..65f6b43 100644
--- a/ImGui.Forms/Controls/Menu/MenuBarRadio.cs
+++ b/ImGui.Forms/Controls/Menu/MenuBarRadio.cs
@@ -19,6 +19,12 @@ public LocalizedString Text
             set => _radioMenu.Text = value;
         }
 
+        public FontResource Font
+        {
+            get => _radioMenu.Font;
+            set => _radioMenu.Font = value;
+        }
+
         public IList<MenuBarCheckBox> CheckItems { get; }
 
         public MenuBarCheckBox SelectedItem { get; private set; }
@@ -37,7 +43,7 @@ public override bool Enabled
 
         #endregion
 
-        public MenuBarRadio(LocalizedString text)
+        public MenuBarRadio(LocalizedString text = default)
         {
             var checkItems = new ObservableList<MenuBarCheckBox>();
             checkItems.ItemAdded += CheckItems_ItemAdded;
@@ -106,15 +112,15 @@ private void CheckItems_ItemSet(object sender, ItemEventArgs<MenuBarCheckBox> e)
 
         private void Item_CheckChanged(object sender, EventArgs e)
         {
-            foreach (var language in _radioMenu.Items.Cast<MenuBarCheckBox>())
+            foreach (MenuBarCheckBox checkbox in _radioMenu.Items.Cast<MenuBarCheckBox>())
             {
-                language.CheckChanged -= Item_CheckChanged;
-                language.Checked = language == sender;
-                language.CheckChanged += Item_CheckChanged;
+                checkbox.CheckChanged -= Item_CheckChanged;
+                checkbox.Checked = checkbox == sender;
+                checkbox.CheckChanged += Item_CheckChanged;
             }
 
             SelectedItem = (MenuBarCheckBox)sender;
-            SelectedItemChanged?.Invoke(this, new EventArgs());
+            SelectedItemChanged?.Invoke(this, EventArgs.Empty);
         }
     }
 }
diff --git a/ImGui.Forms/Controls/MultiLineTextBox.cs b/ImGui.Forms/Controls/MultiLineTextBox.cs
index c720b1d..7d9f67c 100644
--- a/ImGui.Forms/Controls/MultiLineTextBox.cs
+++ b/ImGui.Forms/Controls/MultiLineTextBox.cs
@@ -12,6 +12,11 @@ public class MultiLineTextBox : Component
 
         #region Properties
 
+        /// <summary>
+        /// The size of this component.
+        /// </summary>
+        public Size Size { get; set; } = Size.Parent;
+
         /// <summary>
         /// The text that was set or changed in this component.
         /// </summary>
@@ -43,7 +48,7 @@ public string Text
 
         #endregion
 
-        public override Size GetSize() => Size.Parent;
+        public override Size GetSize() => Size;
 
         protected override void UpdateInternal(Rectangle contentRect)
         {
diff --git a/ImGui.Forms/Controls/PictureBox.cs b/ImGui.Forms/Controls/PictureBox.cs
index b5646ce..8f08656 100644
--- a/ImGui.Forms/Controls/PictureBox.cs
+++ b/ImGui.Forms/Controls/PictureBox.cs
@@ -1,5 +1,4 @@
-using System.Numerics;
-using ImGui.Forms.Controls.Base;
+using ImGui.Forms.Controls.Base;
 using ImGui.Forms.Models;
 using ImGui.Forms.Resources;
 using Veldrid;
@@ -12,6 +11,8 @@ public class PictureBox : Component
 
         #region Properties
 
+        public Size Size { get; set; } = Size.Content;
+
         public ThemedImageResource Image
         {
             get => _baseImg;
@@ -31,7 +32,15 @@ public PictureBox(ThemedImageResource image = default)
 
         public override Size GetSize()
         {
-            return new Size(_baseImg?.Width ?? 0, _baseImg?.Height ?? 0);
+            SizeValue width = Size.Width.IsContentAligned
+                ? SizeValue.Absolute(_baseImg?.Width ?? 0)
+                : Size.Width;
+
+            SizeValue height = Size.Height.IsContentAligned
+                ? SizeValue.Absolute(_baseImg?.Height ?? 0)
+                : Size.Height;
+
+            return new Size(width, height);
         }
 
         protected override void UpdateInternal(Rectangle contentRect)
@@ -39,7 +48,7 @@ protected override void UpdateInternal(Rectangle contentRect)
             if (_baseImg == null || (nint)_baseImg == nint.Zero)
                 return;
 
-            ImGuiNET.ImGui.Image((nint)Image, new Vector2(_baseImg.Width, _baseImg.Height));
+            ImGuiNET.ImGui.Image((nint)Image, contentRect.Size);
         }
     }
 }
diff --git a/ImGui.Forms/Controls/ProgressBar.cs b/ImGui.Forms/Controls/ProgressBar.cs
index c419a9e..26e4815 100644
--- a/ImGui.Forms/Controls/ProgressBar.cs
+++ b/ImGui.Forms/Controls/ProgressBar.cs
@@ -30,10 +30,7 @@ public class ProgressBar : Component
 
         #endregion
 
-        public override Size GetSize()
-        {
-            return Size;
-        }
+        public override Size GetSize() => Size;
 
         protected override void UpdateInternal(Rectangle contentRect)
         {
diff --git a/ImGui.Forms/Controls/TabControl.cs b/ImGui.Forms/Controls/TabControl.cs
index 171fba4..7268599 100644
--- a/ImGui.Forms/Controls/TabControl.cs
+++ b/ImGui.Forms/Controls/TabControl.cs
@@ -93,8 +93,8 @@ protected override async void UpdateInternal(Rectangle contentRect)
                             // Draw content of tab page
                             var yPos = (int)ImGuiNET.ImGui.GetCursorPosY();
 
-                            var pageWidth = page.Content.GetWidth(contentRect.Width);
-                            var pageHeight = page.Content.GetHeight(contentRect.Height - yPos);
+                            var pageWidth = page.Content.GetWidth(contentRect.Width, contentRect.Height - yPos);
+                            var pageHeight = page.Content.GetHeight(contentRect.Width, contentRect.Height - yPos);
 
                             if (ImGuiNET.ImGui.BeginChild($"##{Id}-in", new Vector2(pageWidth, pageHeight), ImGuiChildFlags.None, ImGuiWindowFlags.None))
                                 page.Content.Update(new Rectangle(contentRect.X, contentRect.Y + yPos, pageWidth, pageHeight));
diff --git a/ImGui.Forms/Controls/TextBox.cs b/ImGui.Forms/Controls/TextBox.cs
index 951eefb..f741a31 100644
--- a/ImGui.Forms/Controls/TextBox.cs
+++ b/ImGui.Forms/Controls/TextBox.cs
@@ -131,13 +131,13 @@ protected override void UpdateInternal(Rectangle contentRect)
             {
                 if (ImGuiNET.ImGui.InputText($"##{Id}", ref _text, MaxCharacters, flags))
                     OnTextChanged();
+            }
 
-                // Check if InputText is active and lost focus
-                if (!ImGuiNET.ImGui.IsItemActive() && _activePreviousFrame)
-                    OnFocusLost();
+            // Check if item is active and lost focus
+            if (!ImGuiNET.ImGui.IsItemActive() && _activePreviousFrame)
+                OnFocusLost();
 
-                _activePreviousFrame = ImGuiNET.ImGui.IsItemActive();
-            }
+            _activePreviousFrame = ImGuiNET.ImGui.IsItemActive();
 
             if (isReadonly || !enabled)
                 ImGuiNET.ImGui.PopStyleColor(3);
diff --git a/ImGui.Forms/Controls/Tree/TreeNode.cs b/ImGui.Forms/Controls/Tree/TreeNode.cs
index 7cbf6d8..9ac9a33 100644
--- a/ImGui.Forms/Controls/Tree/TreeNode.cs
+++ b/ImGui.Forms/Controls/Tree/TreeNode.cs
@@ -42,7 +42,7 @@ public class TreeNode<TNodeData> : TreeNode
 
         public bool IsRoot => Parent?._isRoot ?? true;
 
-        public new IList<TreeNode<TNodeData>> Nodes => _nodes;
+        public IList<TreeNode<TNodeData>> Nodes => _nodes;
 
         public TreeNode<TNodeData> Parent { get; private set; }
 
diff --git a/ImGui.Forms/Controls/ZoomablePictureBox.cs b/ImGui.Forms/Controls/ZoomablePictureBox.cs
index db56355..08f9601 100644
--- a/ImGui.Forms/Controls/ZoomablePictureBox.cs
+++ b/ImGui.Forms/Controls/ZoomablePictureBox.cs
@@ -29,6 +29,8 @@ public ThemedImageResource Image
             }
         }
 
+        public Size Size { get; set; } = Size.Parent;
+
         #endregion
 
         #region Events
@@ -42,10 +44,7 @@ public ZoomablePictureBox(ThemedImageResource image = default)
             Image = image;
         }
 
-        public override Size GetSize()
-        {
-            return Size.Parent;
-        }
+        public override Size GetSize() => Size;
 
         public void Zoom(float scale)
         {
diff --git a/ImGui.Forms/Form.cs b/ImGui.Forms/Form.cs
index d09903e..52b51fb 100644
--- a/ImGui.Forms/Form.cs
+++ b/ImGui.Forms/Form.cs
@@ -132,8 +132,8 @@ internal void Update()
             ImGuiNET.ImGui.SetWindowPos(new Vector2(0, menuHeight));
 
             var contentPos = ImGuiNET.ImGui.GetCursorScreenPos();
-            var contentWidth = Content?.GetWidth(Width - (int)Padding.X * 2) ?? 0;
-            var contentHeight = Content?.GetHeight(Height - (int)Padding.Y * 2 - menuHeight) ?? 0;
+            var contentWidth = Content?.GetWidth(Width - (int)Padding.X * 2, Height - (int)Padding.Y * 2 - menuHeight) ?? 0;
+            var contentHeight = Content?.GetHeight(Width - (int)Padding.X * 2, Height - (int)Padding.Y * 2 - menuHeight) ?? 0;
             var contentRect = new Veldrid.Rectangle((int)contentPos.X, (int)contentPos.Y, contentWidth, contentHeight);
 
             Content?.Update(contentRect);
@@ -175,7 +175,7 @@ internal void OnLoad()
 
         internal async Task OnClosing(ClosingEventArgs e)
         {
-            if (Closing == null) 
+            if (Closing == null)
                 return;
 
             await Closing?.Invoke(this, e);
diff --git a/ImGui.Forms/ImGui.Forms.nuspec b/ImGui.Forms/ImGui.Forms.nuspec
index 73aad12..223a9ae 100644
--- a/ImGui.Forms/ImGui.Forms.nuspec
+++ b/ImGui.Forms/ImGui.Forms.nuspec
@@ -2,7 +2,7 @@
 <package >
   <metadata>
     <id>Imgui.Forms</id>
-    <version>1.1.8</version>
+    <version>1.2.0</version>
     <description>A WinForms-inspired object-oriented framework around Dear ImGui (https://github.com/ocornut/imgui)</description>
 
     <authors>onepiecefreak</authors>
diff --git a/ImGui.Forms/Localization/BaseLocalizer.cs b/ImGui.Forms/Localization/BaseLocalizer.cs
index c619613..2620a88 100644
--- a/ImGui.Forms/Localization/BaseLocalizer.cs
+++ b/ImGui.Forms/Localization/BaseLocalizer.cs
@@ -42,6 +42,12 @@ public void ChangeLocale(string locale)
             SetCurrentLocale(locale);
         }
 
+        public bool TryLocalize(string localizationId, out string localization, params object[] args)
+        {
+            localization = Localize(localizationId, args);
+            return localization != UndefinedValue;
+        }
+
         public string Localize(string localizationId, params object[] args)
         {
             // Return localization of current locale
diff --git a/ImGui.Forms/Localization/ILocalizer.cs b/ImGui.Forms/Localization/ILocalizer.cs
index dfbb199..6b45d53 100644
--- a/ImGui.Forms/Localization/ILocalizer.cs
+++ b/ImGui.Forms/Localization/ILocalizer.cs
@@ -11,6 +11,7 @@ public interface ILocalizer
         string GetLanguageName(string locale);
         void ChangeLocale(string locale);
 
+        bool TryLocalize(string localizationId, out string localization, params object[] args);
         string Localize(string localizationId, params object[] args);
     }
 }
diff --git a/ImGui.Forms/Modals/IO/ComboInputBox.cs b/ImGui.Forms/Modals/IO/ComboInputBox.cs
index f75f26f..0b14a1d 100644
--- a/ImGui.Forms/Modals/IO/ComboInputBox.cs
+++ b/ImGui.Forms/Modals/IO/ComboInputBox.cs
@@ -1,21 +1,18 @@
 using System;
 using System.Linq;
-using System.Numerics;
 using System.Threading.Tasks;
 using ImGui.Forms.Controls;
 using ImGui.Forms.Controls.Layouts;
 using ImGui.Forms.Localization;
 using ImGui.Forms.Models;
 using ImGui.Forms.Models.IO;
+using ImGui.Forms.Resources;
 using Veldrid;
 
 namespace ImGui.Forms.Modals.IO
 {
     public class ComboInputBox : Modal
     {
-        private const string Ok_ = "Ok";
-        private const string Cancel_ = "Cancel";
-
         private const int ButtonWidth_ = 75;
 
         private readonly ComboBox<LocalizedString> _comboBox;
@@ -26,15 +23,24 @@ private ComboInputBox(LocalizedString caption, LocalizedString text, LocalizedSt
         {
             #region Controls
 
-            var okButton = new Button { Text = Ok_, Width = ButtonWidth_ };
-            var cancelButton = new Button { Text = Cancel_, Width = ButtonWidth_ };
+            var okButton = new Button { Text = LocalizationResources.Ok(), Width = ButtonWidth_ };
+            var cancelButton = new Button { Text = LocalizationResources.Cancel(), Width = ButtonWidth_ };
 
             var label = new Label { Text = text };
 
-            _comboBox = new ComboBox<LocalizedString>();
+            _comboBox = new ComboBox<LocalizedString> { MaxShowItems = 2 };
             foreach (LocalizedString item in items)
                 _comboBox.Items.Add(item);
 
+            var buttonLayout = new StackLayout { Alignment = Alignment.Horizontal, Size = Size.Content, ItemSpacing = 5 };
+            buttonLayout.Items.Add(okButton);
+            buttonLayout.Items.Add(cancelButton);
+
+            var mainLayout = new StackLayout { Alignment = Alignment.Vertical, Size = Size.Content, ItemSpacing = 5 };
+            mainLayout.Items.Add(label);
+            mainLayout.Items.Add(_comboBox);
+            mainLayout.Items.Add(new StackItem(buttonLayout) { HorizontalAlignment = HorizontalAlignment.Right });
+
             #endregion
 
             #region Events
@@ -59,33 +65,14 @@ private ComboInputBox(LocalizedString caption, LocalizedString text, LocalizedSt
             Result = DialogResult.Cancel;
             Caption = caption;
 
-            Content = new StackLayout
-            {
-                Alignment = Alignment.Vertical,
-                ItemSpacing = 4,
-                Size = new Size(SizeValue.Parent, SizeValue.Content),
-                Items =
-                {
-                    label,
-                    _comboBox,
-                    new StackLayout
-                    {
-                        Alignment = Alignment.Horizontal,
-                        HorizontalAlignment = HorizontalAlignment.Right,
-                        ItemSpacing = 4,
-                        Size=new Size(SizeValue.Parent, SizeValue.Content),
-                        Items =
-                        {
-                            okButton,
-                            cancelButton
-                        }
-                    }
-                }
-            };
-
-            var width = Application.Instance.MainForm.Width * .8f;
-            var height = Content.GetHeight(Application.Instance.MainForm.Height);
-            Size = new Vector2(width, height);
+            Content = mainLayout;
+
+            var mainSize = Application.Instance.MainForm.Size;
+
+            var width = mainLayout.GetWidth((int)mainSize.X, (int)mainSize.Y);
+            var height = mainLayout.GetHeight((int)mainSize.X, (int)mainSize.Y);
+
+            Size = new Size(SizeValue.Absolute(width), SizeValue.Absolute(height));
         }
 
         private void ComboBox_SelectedItemChanged(object sender, EventArgs e)
diff --git a/ImGui.Forms/Modals/IO/InputBox.cs b/ImGui.Forms/Modals/IO/InputBox.cs
index dc6fa99..e3607f0 100644
--- a/ImGui.Forms/Modals/IO/InputBox.cs
+++ b/ImGui.Forms/Modals/IO/InputBox.cs
@@ -1,38 +1,35 @@
 using System;
-using System.Numerics;
 using System.Threading.Tasks;
 using ImGui.Forms.Controls;
 using ImGui.Forms.Controls.Layouts;
 using ImGui.Forms.Localization;
 using ImGui.Forms.Models;
 using ImGui.Forms.Models.IO;
+using ImGui.Forms.Resources;
 using Veldrid;
 
 namespace ImGui.Forms.Modals.IO
 {
     public class InputBox : Modal
     {
-        private const string Ok_ = "Ok";
-        private const string Cancel_ = "Cancel";
-
         private const int ButtonWidth_ = 75;
 
         private readonly TextBox _textBox;
 
         public string Input { get; private set; }
 
-        private InputBox(LocalizedString caption, LocalizedString text, string preset, LocalizedString? placeHolder, int maxCharacters)
+        private InputBox(LocalizedString caption, LocalizedString text, string preset, LocalizedString? placeholder, int maxCharacters)
         {
             #region Controls
 
-            var okButton = new Button { Text = Ok_, Width = ButtonWidth_ };
-            var cancelButton = new Button { Text = Cancel_, Width = ButtonWidth_ };
+            var okButton = new Button { Text = LocalizationResources.Ok(), Width = ButtonWidth_ };
+            var cancelButton = new Button { Text = LocalizationResources.Cancel(), Width = ButtonWidth_ };
 
             var label = new Label { Text = text };
 
             _textBox = new TextBox();
-            if (placeHolder != null)
-                _textBox.Placeholder = placeHolder.Value;
+            if (placeholder != null)
+                _textBox.Placeholder = placeholder.Value;
             if (maxCharacters >= 0)
                 _textBox.MaxCharacters = (uint)maxCharacters;
 
@@ -63,7 +60,7 @@ private InputBox(LocalizedString caption, LocalizedString text, string preset, L
             {
                 Alignment = Alignment.Vertical,
                 ItemSpacing = 4,
-                Size = new Size(SizeValue.Parent, SizeValue.Content),
+                Size = Size.WidthAlign,
                 Items =
                 {
                     label,
@@ -73,7 +70,7 @@ private InputBox(LocalizedString caption, LocalizedString text, string preset, L
                         Alignment = Alignment.Horizontal,
                         HorizontalAlignment = HorizontalAlignment.Right,
                         ItemSpacing = 4,
-                        Size=new Size(SizeValue.Parent, SizeValue.Content),
+                        Size=Size.WidthAlign,
                         Items =
                         {
                             okButton,
@@ -83,9 +80,8 @@ private InputBox(LocalizedString caption, LocalizedString text, string preset, L
                 }
             };
 
-            var width = Application.Instance.MainForm.Width * .8f;
-            var height = Content.GetHeight(Application.Instance.MainForm.Height);
-            Size = new Vector2(width, height);
+            var height = Content.GetHeight(Application.Instance.MainForm.Width, Application.Instance.MainForm.Height);
+            Size = new Size(SizeValue.Relative(.8f), SizeValue.Absolute(height));
         }
 
         private void TextBox_TextChanged(object sender, EventArgs e)
diff --git a/ImGui.Forms/Modals/IO/OpenFileDialog.cs b/ImGui.Forms/Modals/IO/OpenFileDialog.cs
index 222ae3c..155dc33 100644
--- a/ImGui.Forms/Modals/IO/OpenFileDialog.cs
+++ b/ImGui.Forms/Modals/IO/OpenFileDialog.cs
@@ -3,12 +3,12 @@
 using System.Globalization;
 using System.IO;
 using System.Linq;
-using System.Numerics;
 using ImGui.Forms.Controls;
 using ImGui.Forms.Controls.Layouts;
 using ImGui.Forms.Controls.Lists;
 using ImGui.Forms.Controls.Tree;
 using ImGui.Forms.Models;
+using ImGui.Forms.Resources;
 using ImGui.Forms.Support;
 using ImGuiNET;
 
@@ -16,6 +16,9 @@ namespace ImGui.Forms.Modals.IO
 {
     public class OpenFileDialog : Modal
     {
+        private const string ItemTypeDirectory_ = "D";
+        private const string ItemTypeFile_ = "F";
+
         private History<string> _dictHistory;
         private string _currentDir;
 
@@ -53,19 +56,19 @@ public OpenFileDialog()
             _backBtn = new ArrowButton { Direction = ImGuiDir.Left, Enabled = false };
             _forBtn = new ArrowButton { Direction = ImGuiDir.Right, Enabled = false };
             _dirTextBox = new TextBox { Width = .7f };
-            _searchTextBox = new TextBox { Width = .3f, Placeholder = "Search..." };
+            _searchTextBox = new TextBox { Width = .3f, Placeholder = LocalizationResources.Search() };
             _selectedFileTextBox = new TextBox { Width = 1f };
             _fileFilters = new ComboBox<FileFilter>();
 
             _treeView = new TreeView<string> { Size = new Size(.3f, 1f) };
 
             _fileTable = new DataTable<FileEntry> { Size = new Size(.7f, 1f) };
-            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Name, "Name"));
-            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Type, "Type"));
-            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.DateModified.ToString(CultureInfo.CurrentCulture), "Date modified"));
+            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Name, LocalizationResources.ItemName()));
+            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Type, LocalizationResources.ItemType()));
+            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.DateModified.ToString(CultureInfo.CurrentCulture), LocalizationResources.ItemDateModified()));
 
-            var cnlBtn = new Button { Text = "Cancel", Width = 80 };
-            _openBtn = new Button { Text = "Open", Width = 80, Enabled = false };
+            var cancelBtn = new Button { Text = LocalizationResources.Cancel(), Width = 80 };
+            _openBtn = new Button { Text = LocalizationResources.Ok(), Width = 80, Enabled = false };
 
             #endregion
 
@@ -85,16 +88,14 @@ public OpenFileDialog()
 
             _fileFilters.SelectedItemChanged += _fileFilters_SelectedItemChanged;
 
-            cnlBtn.Clicked += CnlBtn_Clicked;
+            cancelBtn.Clicked += CnlBtn_Clicked;
             _openBtn.Clicked += OpenBtnClicked;
 
             #endregion
 
             Result = DialogResult.Cancel;
 
-            var width = (int)Math.Ceiling(Application.Instance.MainForm.Width * .9f);
-            var height = (int)Math.Ceiling(Application.Instance.MainForm.Height * .8f);
-            Size = new Vector2(width, height);
+            Size = new Size(SizeValue.Relative(.9f), SizeValue.Relative(.8f));
 
             Content = new StackLayout
             {
@@ -105,7 +106,7 @@ public OpenFileDialog()
                     new StackLayout
                     {
                         Alignment = Alignment.Horizontal,
-                        Size = new Size(SizeValue.Parent, SizeValue.Content),
+                        Size = Size.WidthAlign,
                         ItemSpacing = 5,
                         Items =
                         {
@@ -128,12 +129,11 @@ public OpenFileDialog()
                     new StackLayout
                     {
                         Alignment = Alignment.Horizontal,
-                        HorizontalAlignment = HorizontalAlignment.Right,
-                        Size = new Size(7f, SizeValue.Content),
+                        Size = Size.WidthAlign,
                         ItemSpacing = 5,
                         Items =
                         {
-                            new Label {Text = "File name:"},
+                            new Label {Text = LocalizationResources.SelectedFile()},
                             _selectedFileTextBox,
                             _fileFilters
                         }
@@ -142,12 +142,12 @@ public OpenFileDialog()
                     {
                         Alignment = Alignment.Horizontal,
                         HorizontalAlignment = HorizontalAlignment.Right,
-                        Size = new Size(SizeValue.Parent, SizeValue.Content),
+                        Size = Size.WidthAlign,
                         ItemSpacing = 5,
                         Items =
                         {
                             new StackItem(_openBtn),
-                            new StackItem(cnlBtn)
+                            new StackItem(cancelBtn)
                         }
                     }
                 }
@@ -164,8 +164,10 @@ protected override void ShowInternal()
             _selectedFileTextBox.Text = InitialFileName;
 
             // Initialize file tree and file view
+            var desktopDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
             var userDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
-            _treeView.Nodes.Add(new TreeNode<string> { Text = "Desktop", Data = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Nodes = { new TreeNode<string>() } });
+
+            _treeView.Nodes.Add(new TreeNode<string> { Text = Path.GetFileName(desktopDir), Data = desktopDir, Nodes = { new TreeNode<string>() } });
             _treeView.Nodes.Add(new TreeNode<string> { Text = Path.GetFileName(userDir), Data = userDir, Nodes = { new TreeNode<string>() } });
 
             UpdateFileView();
@@ -192,7 +194,7 @@ private string GetNodePath(TreeNode<string> node)
         private IEnumerable<FileEntry> GetDirectories(string dir)
         {
             return Directory.EnumerateDirectories(dir).Select(x => new FileEntry
-            { Name = Path.GetFileName(x), Type = "D", DateModified = Directory.GetLastAccessTime(x) });
+            { Name = Path.GetFileName(x), Type = ItemTypeDirectory_, DateModified = Directory.GetLastAccessTime(x) });
         }
 
         private IEnumerable<FileEntry> GetFiles(string dir)
@@ -205,9 +207,13 @@ private IEnumerable<FileEntry> GetFiles(string dir)
 
             // Apply extension filter, if set
             if (!useSearch && _fileFilters.SelectedItem != null)
-                files = files.Where(f => GetFileExtensions(f).Any(e => _fileFilters.SelectedItem.Content.Extensions.Contains(e)));
+            {
+                IList<string> extensions = _fileFilters.SelectedItem.Content.Extensions;
+                if (extensions.All(e => e != "*"))
+                    files = files.Where(f => GetFileExtensions(f).Any(e => extensions.Contains(e)));
+            }
 
-            return files.Select(x => new FileEntry { Name = Path.GetFileName(x), Type = "F", DateModified = File.GetLastWriteTime(x) });
+            return files.Select(x => new FileEntry { Name = Path.GetFileName(x), Type = ItemTypeFile_, DateModified = File.GetLastWriteTime(x) });
         }
 
         private string[] GetFileExtensions(string filePath)
@@ -254,7 +260,7 @@ private void UpdateButtonEnablement()
 
         private void Filters_ItemAdded(object sender, ItemEventArgs<FileFilter> e)
         {
-            _fileFilters.Items.Add(new ComboBoxItem<FileFilter>(e.Item));
+            _fileFilters.Items.Add(new DropDownItem<FileFilter>(e.Item));
         }
 
         private void Filters_ItemRemoved(object sender, ItemEventArgs<FileFilter> e)
@@ -264,12 +270,12 @@ private void Filters_ItemRemoved(object sender, ItemEventArgs<FileFilter> e)
 
         private void Filters_ItemInserted(object sender, ItemEventArgs<FileFilter> e)
         {
-            _fileFilters.Items.Insert(e.Index, new ComboBoxItem<FileFilter>(e.Item));
+            _fileFilters.Items.Insert(e.Index, new DropDownItem<FileFilter>(e.Item));
         }
 
         private void Filters_ItemSet(object sender, ItemEventArgs<FileFilter> e)
         {
-            _fileFilters.Items[e.Index] = new ComboBoxItem<FileFilter>(e.Item);
+            _fileFilters.Items[e.Index] = new DropDownItem<FileFilter>(e.Item);
         }
 
         private void _fileFilters_SelectedItemChanged(object sender, EventArgs e)
diff --git a/ImGui.Forms/Modals/IO/SaveFileDialog.cs b/ImGui.Forms/Modals/IO/SaveFileDialog.cs
index 01dca2b..2a23a4a 100644
--- a/ImGui.Forms/Modals/IO/SaveFileDialog.cs
+++ b/ImGui.Forms/Modals/IO/SaveFileDialog.cs
@@ -3,13 +3,13 @@
 using System.Globalization;
 using System.IO;
 using System.Linq;
-using System.Numerics;
 using System.Threading.Tasks;
 using ImGui.Forms.Controls;
 using ImGui.Forms.Controls.Layouts;
 using ImGui.Forms.Controls.Lists;
 using ImGui.Forms.Controls.Tree;
 using ImGui.Forms.Models;
+using ImGui.Forms.Resources;
 using ImGui.Forms.Support;
 using ImGuiNET;
 
@@ -17,6 +17,9 @@ namespace ImGui.Forms.Modals.IO
 {
     public class SaveFileDialog : Modal
     {
+        private const string ItemTypeDirectory_ = "D";
+        private const string ItemTypeFile_ = "F";
+
         private History<string> _dictHistory;
         private string _currentDir;
 
@@ -42,19 +45,19 @@ public SaveFileDialog(string filePath = null)
             _backBtn = new ArrowButton { Direction = ImGuiDir.Left, Enabled = false };
             _forBtn = new ArrowButton { Direction = ImGuiDir.Right, Enabled = false };
             _dirTextBox = new TextBox { Width = .7f };
-            _searchTextBox = new TextBox { Width = .3f, Placeholder = "Search..." };
-            _selectedFileTextBox = new TextBox { Width = .7f };
+            _searchTextBox = new TextBox { Width = .3f, Placeholder = LocalizationResources.Search() };
+            _selectedFileTextBox = new TextBox { Width = 1f };
 
             _treeView = new TreeView<string> { Size = new Size(.3f, 1f) };
             _treeView.NodeExpanded += _treeView_NodeExpanded;
 
             _fileTable = new DataTable<FileEntry> { Size = new Size(.7f, 1f) };
-            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Name, "Name"));
-            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Type, "Type"));
-            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.DateModified.ToString(CultureInfo.CurrentCulture), "Date modified"));
+            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Name, LocalizationResources.ItemName()));
+            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.Type, LocalizationResources.ItemType()));
+            _fileTable.Columns.Add(new DataTableColumn<FileEntry>(x => x.DateModified.ToString(CultureInfo.CurrentCulture), LocalizationResources.ItemDateModified()));
 
-            var cnlBtn = new Button { Text = "Cancel", Width = 80 };
-            _saveBtn = new Button { Text = "Save", Width = 80, Enabled = !string.IsNullOrEmpty(filePath) };
+            var cancelBtn = new Button { Text = LocalizationResources.Cancel(), Width = 80 };
+            _saveBtn = new Button { Text = LocalizationResources.Save(), Width = 80, Enabled = !string.IsNullOrEmpty(filePath) };
 
             #endregion
 
@@ -78,14 +81,12 @@ public SaveFileDialog(string filePath = null)
             _fileTable.DoubleClicked += _fileTable_DoubleClicked;
             _fileTable.SelectedRowsChanged += _fileTable_SelectedRowsChanged;
 
-            cnlBtn.Clicked += CnlBtn_Clicked;
+            cancelBtn.Clicked += CnlBtn_Clicked;
             _saveBtn.Clicked += SaveBtnClicked;
 
             #endregion
 
-            var width = (int)Math.Ceiling(Application.Instance.MainForm.Width * .9f);
-            var height = (int)Math.Ceiling(Application.Instance.MainForm.Height * .8f);
-            Size = new Vector2(width, height);
+            Size = new Size(SizeValue.Relative(.9f), SizeValue.Relative(.8f));
 
             Content = new StackLayout
             {
@@ -97,7 +98,7 @@ public SaveFileDialog(string filePath = null)
                     new StackLayout
                     {
                         Alignment = Alignment.Horizontal,
-                        Size = new Size(SizeValue.Parent, SizeValue.Content),
+                        Size = Size.WidthAlign,
                         ItemSpacing = 5,
                         Items =
                         {
@@ -124,12 +125,11 @@ public SaveFileDialog(string filePath = null)
                     new StackLayout
                     {
                         Alignment = Alignment.Horizontal,
-                        HorizontalAlignment = HorizontalAlignment.Right,
-                        Size = new Size(7f, SizeValue.Content),
+                        Size = Size.WidthAlign,
                         ItemSpacing = 5,
                         Items =
                         {
-                            new Label {Text = "File name:"},
+                            new Label {Text = LocalizationResources.SelectedFile()},
                             _selectedFileTextBox
                         }
                     },
@@ -139,12 +139,12 @@ public SaveFileDialog(string filePath = null)
                     {
                         Alignment = Alignment.Horizontal,
                         HorizontalAlignment = HorizontalAlignment.Right,
-                        Size = new Size(SizeValue.Parent, SizeValue.Content),
+                        Size = Size.WidthAlign,
                         ItemSpacing = 5,
                         Items =
                         {
                             new StackItem(_saveBtn),
-                            new StackItem(cnlBtn)
+                            new StackItem(cancelBtn)
                         }
                     }
                 }
@@ -160,10 +160,13 @@ protected override void ShowInternal()
             _dirTextBox.Text = _currentDir;
 
             // Initialize file tree and file view
+            var desktopDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
             var userDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
-            _treeView.Nodes.Add(new TreeNode<string> { Text = "Desktop", Data = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Nodes = { new TreeNode<string>() } });
+
+            _treeView.Nodes.Add(new TreeNode<string> { Text = Path.GetFileName(desktopDir), Data = desktopDir, Nodes = { new TreeNode<string>() } });
             _treeView.Nodes.Add(new TreeNode<string> { Text = Path.GetFileName(userDir), Data = userDir, Nodes = { new TreeNode<string>() } });
 
+
             UpdateFileView();
         }
 
@@ -188,7 +191,7 @@ private string GetNodePath(TreeNode<string> node)
         private IEnumerable<FileEntry> GetDirectories(string dir)
         {
             return Directory.EnumerateDirectories(dir).Select(x => new FileEntry
-            { Name = Path.GetFileName(x), Type = "D", DateModified = Directory.GetLastAccessTime(x) });
+            { Name = Path.GetFileName(x), Type = ItemTypeDirectory_, DateModified = Directory.GetLastAccessTime(x) });
         }
 
         private IEnumerable<FileEntry> GetFiles(string dir)
@@ -197,7 +200,7 @@ private IEnumerable<FileEntry> GetFiles(string dir)
             var searchTerm = string.IsNullOrEmpty(_searchTextBox.Text) ? "*" : _searchTextBox.Text;
             var files = Directory.EnumerateFiles(dir, searchTerm);
 
-            return files.Select(x => new FileEntry { Name = Path.GetFileName(x), Type = "F", DateModified = File.GetLastWriteTime(x) });
+            return files.Select(x => new FileEntry { Name = Path.GetFileName(x), Type = ItemTypeFile_, DateModified = File.GetLastWriteTime(x) });
         }
 
         private async Task<bool> ShouldOverwrite(string path)
@@ -205,7 +208,7 @@ private async Task<bool> ShouldOverwrite(string path)
             if (!File.Exists(path))
                 return true;
 
-            return await MessageBox.ShowYesNoAsync("File exists", $"Do you want to overwrite file {Path.GetFileName(path)}?") != DialogResult.No;
+            return await MessageBox.ShowYesNoAsync(LocalizationResources.ReplaceFileCaption(), LocalizationResources.ReplaceFileText(Path.GetFileName(path))) != DialogResult.No;
         }
 
         #endregion
diff --git a/ImGui.Forms/Modals/IO/SelectFolderDialog.cs b/ImGui.Forms/Modals/IO/SelectFolderDialog.cs
index 60a6be6..554f4be 100644
--- a/ImGui.Forms/Modals/IO/SelectFolderDialog.cs
+++ b/ImGui.Forms/Modals/IO/SelectFolderDialog.cs
@@ -2,22 +2,16 @@
 using System.IO;
 using System.Linq;
 using System.Numerics;
-using System.Threading.Tasks;
 using ImGui.Forms.Controls;
 using ImGui.Forms.Controls.Layouts;
 using ImGui.Forms.Controls.Tree;
 using ImGui.Forms.Models;
+using ImGui.Forms.Resources;
 
 namespace ImGui.Forms.Modals.IO
 {
     public class SelectFolderDialog : Modal
     {
-        private const string Ok_ = "Ok";
-        private const string Cancel_ = "Cancel";
-        private const string NewFolder_ = "Create new folder";
-
-        private const int ButtonWidth_ = 75;
-
         private TreeView<string> _treeView;
 
         private Button _newFolderButton;
@@ -32,9 +26,9 @@ public SelectFolderDialog()
 
             _treeView = new TreeView<string>();
 
-            _newFolderButton = new Button { Text = NewFolder_, Enabled = false };
-            _okButton = new Button { Text = Ok_, Width = ButtonWidth_, Enabled = false };
-            _cancelButton = new Button { Text = Cancel_, Width = ButtonWidth_ };
+            _newFolderButton = new Button { Text = LocalizationResources.CreateFolderCaption(), Enabled = false, Padding = new Vector2(5, 2) };
+            _okButton = new Button { Text = LocalizationResources.Ok(), Width = 80, Enabled = false };
+            _cancelButton = new Button { Text = LocalizationResources.Cancel(), Width = 80 };
 
             #endregion
 
@@ -51,9 +45,7 @@ public SelectFolderDialog()
 
             #region Main content
 
-            var width = Application.Instance.MainForm.Width * .9f;
-            var height = Application.Instance.MainForm.Height * .8f;
-            Size = new Vector2(width, height);
+            Size = new Size(SizeValue.Relative(.9f), SizeValue.Relative(.8f));
 
             Result = DialogResult.Cancel;
             Content = new StackLayout
@@ -96,8 +88,10 @@ protected override void ShowInternal()
             Directory = GetInitialDirectory();
 
             // Initialize file tree and file view
+            var desktopDir = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
             var userDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
-            _treeView.Nodes.Add(new TreeNode<string> { Text = "Desktop", Data = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), Nodes = { new TreeNode<string>() } });
+
+            _treeView.Nodes.Add(new TreeNode<string> { Text = Path.GetFileName(desktopDir), Data = desktopDir, Nodes = { new TreeNode<string>() } });
             _treeView.Nodes.Add(new TreeNode<string> { Text = Path.GetFileName(userDir), Data = userDir, Nodes = { new TreeNode<string>() } });
 
             foreach (var drive in DriveInfo.GetDrives())
@@ -109,7 +103,7 @@ protected override void ShowInternal()
         private async void _newFolderButton_Clicked(object sender, EventArgs e)
         {
             var path = GetNodePath(_treeView.SelectedNode);
-            var newFolderName = await InputBox.ShowAsync("Create folder", "New folder name:");
+            var newFolderName = await InputBox.ShowAsync(LocalizationResources.CreateFolderCaption(), LocalizationResources.CreateFolderText());
 
             if (string.IsNullOrEmpty(newFolderName))
                 return;
@@ -171,7 +165,7 @@ private void _treeView_NodeExpanded(object sender, NodeEventArgs<string> e)
 
         #endregion
 
-        #region Support method
+        #region Support methods
 
         private string GetInitialDirectory()
         {
diff --git a/ImGui.Forms/Modals/MessageBox.cs b/ImGui.Forms/Modals/MessageBox.cs
index 34ae2c4..e6aa9c4 100644
--- a/ImGui.Forms/Modals/MessageBox.cs
+++ b/ImGui.Forms/Modals/MessageBox.cs
@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Numerics;
 using System.Threading.Tasks;
 using ImGui.Forms.Controls;
 using ImGui.Forms.Controls.Base;
@@ -13,11 +12,6 @@ namespace ImGui.Forms.Modals
 {
     public class MessageBox : Modal
     {
-        private const string Ok_ = "Ok";
-        private const string Yes_ = "Yes";
-        private const string No_ = "No";
-        private const string Cancel_ = "Cancel";
-
         private const int ButtonWidth_ = 75;
 
         private MessageBox(LocalizedString caption, LocalizedString text, MessageBoxType type, MessageBoxButton buttons)
@@ -76,27 +70,28 @@ private void CreateLayout(LocalizedString caption, LocalizedString text, Message
             var msgType = GetTypeImage(type);
             var msgLabel = new Label { Text = text };
 
-            var messageLayout = new StackLayout { Alignment = Alignment.Horizontal, Size = Models.Size.Content, ItemSpacing = 5 };
+            var messageLayout = new StackLayout { Alignment = Alignment.Horizontal, Size = Size.Content, ItemSpacing = 5 };
             if (msgType != null)
                 messageLayout.Items.Add(new StackItem(msgType) { VerticalAlignment = VerticalAlignment.Center });
             messageLayout.Items.Add(new StackItem(msgLabel) { VerticalAlignment = VerticalAlignment.Center });
 
             // Prepare buttons
-            var buttonLayout = new StackLayout { Alignment = Alignment.Horizontal, HorizontalAlignment = HorizontalAlignment.Right, Size = new Models.Size(SizeValue.Parent,SizeValue.Content), ItemSpacing = 5 };
+            var buttonLayout = new StackLayout { Alignment = Alignment.Horizontal, Size = Size.Content, ItemSpacing = 5 };
             foreach (var button in GetButtons(buttons))
                 buttonLayout.Items.Add(button);
 
             // Prepare main layout
-            var mainLayout = new StackLayout { Alignment = Alignment.Vertical, ItemSpacing = 5, Size = Models.Size.Content };
-            mainLayout.Items.Add(new StackItem(messageLayout));
-            mainLayout.Items.Add(buttonLayout);
+            var mainLayout = new StackLayout { Alignment = Alignment.Vertical, Size = Size.Content, ItemSpacing = 5 };
+            mainLayout.Items.Add(messageLayout);
+            mainLayout.Items.Add(new StackItem(buttonLayout) { HorizontalAlignment = HorizontalAlignment.Right });
 
             // Add modal
-            var mainSize = new Vector2(Application.Instance.MainForm.Width, Application.Instance.MainForm.Height);
+            var mainSize = Application.Instance.MainForm.Size;
+
+            var width = mainLayout.GetWidth((int)mainSize.X, (int)mainSize.Y) + (msgType?.GetWidth((int)mainSize.X, (int)mainSize.Y) ?? 0) + messageLayout.ItemSpacing;
+            var height = mainLayout.GetHeight((int)mainSize.X, (int)mainSize.Y);
 
-            var width = msgLabel.GetWidth((int)mainSize.X) + (msgType?.GetWidth((int)mainSize.X) ?? 0) + messageLayout.ItemSpacing;
-            var height = mainLayout.GetHeight((int)mainSize.Y);
-            Size = new Vector2(width, height);
+            Size = new Size(SizeValue.Absolute(width), SizeValue.Absolute(height));
             Content = mainLayout;
         }
 
@@ -126,15 +121,16 @@ private IEnumerable<Button> GetButtons(MessageBoxButton buttons)
         {
             if (buttons.HasFlag(MessageBoxButton.Ok))
             {
-                var okButton = new Button { Text = Ok_, Width = ButtonWidth_ };
-                okButton.Clicked += (s, e) => Close();
+                var okButton = new Button { Text = LocalizationResources.Ok(), Width = SizeValue.Absolute(ButtonWidth_) };
+                okButton.Clicked += (_, _) => Close();
 
                 yield return okButton;
             }
+
             if (buttons.HasFlag(MessageBoxButton.Yes))
             {
-                var yesButton = new Button { Text = Yes_, Width = ButtonWidth_ };
-                yesButton.Clicked += (s, e) =>
+                var yesButton = new Button { Text = LocalizationResources.Yes(), Width = SizeValue.Absolute(ButtonWidth_) };
+                yesButton.Clicked += (_, _) =>
                 {
                     Result = DialogResult.Yes;
                     Close();
@@ -142,10 +138,11 @@ private IEnumerable<Button> GetButtons(MessageBoxButton buttons)
 
                 yield return yesButton;
             }
+
             if (buttons.HasFlag(MessageBoxButton.No))
             {
-                var noButton = new Button { Text = No_, Width = ButtonWidth_ };
-                noButton.Clicked += (s, e) =>
+                var noButton = new Button { Text = LocalizationResources.No(), Width = SizeValue.Absolute(ButtonWidth_) };
+                noButton.Clicked += (_, _) =>
                 {
                     Result = DialogResult.No;
                     Close();
@@ -153,16 +150,17 @@ private IEnumerable<Button> GetButtons(MessageBoxButton buttons)
 
                 yield return noButton;
             }
+
             if (buttons.HasFlag(MessageBoxButton.Cancel))
             {
-                var noButton = new Button { Text = Cancel_, Width = ButtonWidth_ };
-                noButton.Clicked += (s, e) =>
+                var cancelButton = new Button { Text = LocalizationResources.Cancel(), Width = SizeValue.Absolute(ButtonWidth_) };
+                cancelButton.Clicked += (_, _) =>
                 {
                     Result = DialogResult.Cancel;
                     Close();
                 };
 
-                yield return noButton;
+                yield return cancelButton;
             }
         }
     }
diff --git a/ImGui.Forms/Modals/Modal.cs b/ImGui.Forms/Modals/Modal.cs
index 4320864..f36239b 100644
--- a/ImGui.Forms/Modals/Modal.cs
+++ b/ImGui.Forms/Modals/Modal.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Numerics;
+using System.Numerics;
 using System.Threading;
 using System.Threading.Tasks;
 using ImGui.Forms.Controls.Base;
@@ -12,7 +11,7 @@
 
 namespace ImGui.Forms.Modals
 {
-    public class Modal : Component
+    public abstract class Modal : Component
     {
         private CancellationTokenSource _tokenSource;
         private bool _shouldClose;
@@ -30,19 +29,9 @@ public class Modal : Component
 
         protected DialogResult Result { get; set; }
 
-        public Vector2 Size { get; set; } = new(200, 80);
-        public int Width => (int)Size.X;
-        public int Height => (int)Size.Y;
+        public Size Size { get; set; } = new(SizeValue.Absolute(200), SizeValue.Absolute(80));
 
-        public override Size GetSize()
-        {
-            return new Size(Width, Height);
-        }
-
-        public int GetHeaderHeight()
-        {
-            return TextMeasurer.GetCurrentLineHeight(withDescent: true) + 6;
-        }
+        public override Size GetSize() => Size;
 
         protected override async void UpdateInternal(Rectangle contentRect)
         {
@@ -116,6 +105,11 @@ public async void Close()
             _shouldClose = !await ShouldCancelClose();
         }
 
+        private int GetHeaderHeight()
+        {
+            return TextMeasurer.GetCurrentLineHeight(withDescent: true) + 6;
+        }
+
         // HINT: Only gets executed if _shouldClose is set to true
         private async Task CloseCore()
         {
@@ -145,10 +139,13 @@ internal static void DrawModal(Modal modal)
 
             var form = Application.Instance.MainForm;
 
-            var modalPos = new Vector2((form.Width - modal.Width) / 2f, (form.Height - modal.Height - modal.GetHeaderHeight()) / 2f);
+            var modalWidth = GetDimension(modal.Size.Width, form.Width);
+            var modalHeight = GetDimension(modal.Size.Height, form.Height);
+
+            var modalPos = new Vector2((form.Width - modalWidth) / 2f, (form.Height - modalHeight - modal.GetHeaderHeight()) / 2f);
             var contentPos = modalPos + new Vector2(form.Padding.X, modal.GetHeaderHeight() + form.Padding.Y);
 
-            var contentSize = new Vector2(modal.Width, modal.Height);
+            var contentSize = new Vector2(modalWidth, modalHeight);
             var modalSize = contentSize + new Vector2(form.Padding.X * 2, modal.GetHeaderHeight() + form.Padding.Y * 2);
 
             ImGuiNET.ImGui.SetNextWindowPos(modalPos);
diff --git a/ImGui.Forms/Resources/LocalizationResources.cs b/ImGui.Forms/Resources/LocalizationResources.cs
new file mode 100644
index 0000000..8d4a421
--- /dev/null
+++ b/ImGui.Forms/Resources/LocalizationResources.cs
@@ -0,0 +1,63 @@
+using ImGui.Forms.Localization;
+
+namespace ImGui.Forms.Resources
+{
+    static class LocalizationResources
+    {
+        private const string Ok_ = "Ok";
+        private const string Cancel_ = "Cancel";
+        private const string Yes_ = "Yes";
+        private const string No_ = "No";
+        private const string Save_ = "Save";
+        private const string SearchPlaceholder_ = "Search...";
+        private const string ItemName_ = "Name";
+        private const string ItemType_ = "Type";
+        private const string ItemDateModified_ = "Date Modified";
+        private const string SelectedFile_ = "File Name:";
+        private const string ReplaceFileCaption_ = "File exists";
+        private const string ReplaceFileText_ = "Do you want to overwrite file {0}?";
+        private const string CreateFolderCaption_ = "Create folder";
+        private const string CreateFolderText_ = "New folder name:";
+
+        private const string OkIdentifier_ = "ImGui.Button.Ok";
+        private const string CancelIdentifier_ = "ImGui.Button.Cancel";
+        private const string YesIdentifier_ = "ImGui.Button.Yes";
+        private const string NoIdentifier_ = "ImGui.Button.No";
+        private const string SaveIdentifier_ = "ImGui.Button.Save";
+        private const string SearchIdentifier_ = "ImGui.FileDialog.Search";
+        private const string ItemNameIdentifier_ = "ImGui.FileDialog.Name";
+        private const string ItemTypeIdentifier_ = "ImGui.FileDialog.Type";
+        private const string ItemDateModifiedIdentifier_ = "ImGui.FileDialog.DateModified";
+        private const string SelectedFileIdentifier_ = "ImGui.FileDialog.SelectedFile";
+        private const string ReplaceFileCaptionIdentifier_ = "ImGui.FileDialog.ReplaceFile.Caption";
+        private const string ReplaceFileTextIdentifier_ = "ImGui.FileDialog.ReplaceFile.Text";
+        private const string CreateFolderCaptionIdentifier_ = "ImGui.FileDialog.CreateFolder.Caption";
+        private const string CreateFolderTextIdentifier_ = "ImGui.FileDialog.CreateFolder.Text";
+
+        public static string Ok() => Localize(OkIdentifier_, Ok_);
+        public static string Cancel() => Localize(CancelIdentifier_, Cancel_);
+        public static string Yes() => Localize(YesIdentifier_, Yes_);
+        public static string No() => Localize(NoIdentifier_, No_);
+        public static string Save() => Localize(SaveIdentifier_, Save_);
+        public static string Search() => Localize(SearchIdentifier_, SearchPlaceholder_);
+        public static string ItemName() => Localize(ItemNameIdentifier_, ItemName_);
+        public static string ItemType() => Localize(ItemTypeIdentifier_, ItemType_);
+        public static string ItemDateModified() => Localize(ItemDateModifiedIdentifier_, ItemDateModified_);
+        public static string SelectedFile() => Localize(SelectedFileIdentifier_, SelectedFile_);
+        public static string ReplaceFileCaption() => Localize(ReplaceFileCaptionIdentifier_, ReplaceFileCaption_);
+        public static string ReplaceFileText(string path) => Localize(ReplaceFileTextIdentifier_, ReplaceFileText_, path);
+        public static string CreateFolderCaption() => Localize(CreateFolderCaptionIdentifier_, CreateFolderCaption_);
+        public static string CreateFolderText() => Localize(CreateFolderTextIdentifier_, CreateFolderText_);
+
+        private static string Localize(string localizationId, string fallback, params object[] args)
+        {
+            ILocalizer localizer = Application.Instance.Localizer;
+            var text = string.Empty;
+
+            if (!localizer?.TryLocalize(localizationId, out text, args) ?? true)
+                text = fallback;
+
+            return text;
+        }
+    }
+}
diff --git a/ImGui.Forms/Resources/TextMeasurer.cs b/ImGui.Forms/Resources/TextMeasurer.cs
index 66b5836..4de1ad4 100644
--- a/ImGui.Forms/Resources/TextMeasurer.cs
+++ b/ImGui.Forms/Resources/TextMeasurer.cs
@@ -1,7 +1,6 @@
 using ImGuiNET;
 using System;
 using System.Numerics;
-using static System.Net.Mime.MediaTypeNames;
 
 namespace ImGui.Forms.Resources
 {