FishUI 1.0.0
See the version list below for details.
dotnet add package FishUI --version 1.0.0
NuGet\Install-Package FishUI -Version 1.0.0
<PackageReference Include="FishUI" Version="1.0.0" />
<PackageVersion Include="FishUI" Version="1.0.0" />
<PackageReference Include="FishUI" />
paket add FishUI --version 1.0.0
#r "nuget: FishUI, 1.0.0"
#:package FishUI@1.0.0
#addin nuget:?package=FishUI&version=1.0.0
#tool nuget:?package=FishUI&version=1.0.0
FishUI
A dependency-free, immediate-mode-inspired GUI library for .NET applications with backend-agnostic rendering.
Uses the GWEN Skin atlas for theming.
Table of Contents
- Overview
- Screenshots
- Features
- Projects
- Quick Start
- Control Examples
- Layout & Positioning
- Theming
- Serialization
- Virtual Cursor
- Integrating with Existing Game Loops
- FishUIEditor
- Running Samples
- Documentation
- Requirements
- Project Structure
- License
Overview
FishUI is a flexible GUI framework that separates UI logic from rendering, allowing integration with any graphics library. It provides a comprehensive set of controls suitable for game development, tools, and applications.
Key Principles:
- Backend Agnostic: Implement your own graphics and input handlers via simple interfaces
- Dependency Free: Core library has no external dependencies except YamlDotNet for serialization
- Game-Ready: Designed for real-time applications with features like virtual cursor support
- Themeable: YAML-based theme system with atlas/9-slice support
Screenshots
<p align="center"> <img src="screenshots/new/anim_system.gif" width="600" alt="Animation & Particle System"/> </p>
<p align="center"> <img src="screenshots/new/26.png" width="400" alt="Windows & Dialogs"/> <img src="screenshots/new/26_2.png" width="400" alt="Windows & Dialogs"/> </p> <p align="center"> <img src="screenshots/new/1.png" width="400" alt="Animations"/> <img src="screenshots/new/3.png" width="400" alt="Button Variants"/> </p> <p align="center"> <img src="screenshots/new/4.png" width="400" alt="DataGrid"/> <img src="screenshots/new/5.png" width="400" alt="DatePicker"/> </p> <p align="center"> <img src="screenshots/new/6.png" width="400" alt="DropDown"/> <img src="screenshots/new/7.png" width="400" alt="Editor Layout"/> </p> <p align="center"> <img src="screenshots/new/9.png" width="400" alt="Game Main Menu"/> <img src="screenshots/new/10.png" width="400" alt="Gauges"/> </p> <p align="center"> <img src="screenshots/new/13.png" width="400" alt="LayoutSystem"/> <img src="screenshots/new/14.png" width="400" alt="LineChart"/> </p> <p align="center"> <img src="screenshots/new/16.png" width="400" alt="MenuBar"/> <img src="screenshots/new/17.png" width="400" alt="MultiLineEditbox"/> </p> <p align="center"> <img src="screenshots/new/18.png" width="400" alt="PropertyGrid"/> <img src="screenshots/new/19.png" width="400" alt="ScrollablePane"/> </p> <p align="center"> <img src="screenshots/new/21.png" width="400" alt="SpreadsheetGrid"/> <img src="screenshots/new/game_window_sample.png" width="400" alt="Game Window Sample"/> </p>
Features
Controls (47+ Built-in)
| Category | Controls |
|---|---|
| Input | Button, Textbox, CheckBox, RadioButton, ToggleSwitch, Slider, NumericUpDown, MultiLineEditbox |
| Selection | ListBox, DropDown (ComboBox), TreeView, SelectionBox, DatePicker, TimePicker |
| Display | Label, StaticText, ImageBox, AnimatedImageBox, ProgressBar, LineChart, Timeline, BigDigitDisplay, ToastNotification |
| Containers | Panel, Window, GroupBox, TabControl, ScrollablePane, StackLayout, FlowLayout, GridLayout |
| Navigation | ScrollBarV, ScrollBarH, MenuBar, ContextMenu, MenuItem |
| Gauges | RadialGauge, BarGauge, VUMeter |
| Data | DataGrid, SpreadsheetGrid, PropertyGrid, ItemListbox |
| Effects | ParticleEmitter |
| Utility | Tooltip, Titlebar |
Framework Features
- Layout System: Absolute positioning, anchoring, margins/padding, StackLayout, FlowLayout, GridLayout
- Theme System: YAML themes with atlas regions, 9-slice/NPatch rendering, color overrides, inheritance
- Serialization: Save/load UI layouts to YAML files with event handler binding
- Animation: Built-in animation system with easing functions, tween helpers, and particle effects
- Input: Mouse, keyboard, touch, and virtual cursor (gamepad/keyboard navigation)
- Events: Control events, serializable event handlers, event broadcasting
- UI Scaling: Resolution-independent UI with configurable scale factor
Projects
| Project | Description |
|---|---|
| FishUI | Core library - all controls and interfaces |
| FishUIEditor | Visual layout editor for designing FishUI interfaces |
| FishUIDemos | Sample implementations using ISample interface |
| FishUISample | Raylib-based sample runner with GUI chooser |
Quick Start
1. Implement Required Interfaces
FishUI requires three interfaces for your graphics backend:
// Graphics rendering
public class MyGfx : IFishUIGfx
{
public int ScreenWidth { get; set; }
public int ScreenHeight { get; set; }
public void Init() { }
public void BeginDrawing(float dt) { }
public void EndDrawing() { }
public ImageRef LoadImage(string path) { /* ... */ }
public FontRef LoadFont(string path, int size) { /* ... */ }
public void DrawRectangle(Vector2 pos, Vector2 size, FishColor color) { /* ... */ }
public void DrawImage(ImageRef img, Vector2 pos, float rot, float scale, FishColor color) { /* ... */ }
public void DrawNPatch(NPatch patch, Vector2 pos, Vector2 size, FishColor color) { /* ... */ }
public void DrawText(FontRef font, string text, Vector2 pos) { /* ... */ }
// ... see IFishUIGfx for full interface
}
// Input handling
public class MyInput : IFishUIInput
{
public Vector2 GetMousePosition() { /* ... */ }
public bool IsMouseDown(FishMouseButton button) { /* ... */ }
public bool IsMousePressed(FishMouseButton button) { /* ... */ }
public bool IsKeyDown(FishKey key) { /* ... */ }
public bool IsKeyPressed(FishKey key) { /* ... */ }
public string GetTextInput() { /* ... */ }
// ... see IFishUIInput for full interface
}
// Event broadcasting (optional)
public class MyEvents : IFishUIEvents
{
public void Broadcast(FishUI ui, Control sender, string eventName, object[] data) { /* ... */ }
}
2. Initialize FishUI
FishUISettings settings = new FishUISettings();
IFishUIGfx gfx = new MyGfx(800, 600);
IFishUIInput input = new MyInput();
IFishUIEvents events = new MyEvents();
FishUI.FishUI ui = new FishUI.FishUI(settings, gfx, input, events);
ui.Init();
// Load a theme
settings.LoadTheme("data/themes/gwen.yaml", applyImmediately: true);
3. Add Controls
// Simple button with event
Button btn = new Button();
btn.Text = "Click Me";
btn.Position = new Vector2(100, 100);
btn.Size = new Vector2(150, 40);
btn.OnButtonPressed += (sender, mouseBtn, pos) => Console.WriteLine("Clicked!");
ui.AddControl(btn);
// Panel with children
Panel panel = new Panel();
panel.Position = new Vector2(10, 10);
panel.Size = new Vector2(300, 200);
ui.AddControl(panel);
CheckBox check = new CheckBox("Enable Feature");
check.Position = new Vector2(10, 10);
panel.AddChild(check);
// ListBox with items
ListBox list = new ListBox();
list.Position = new Vector2(10, 50);
list.Size = new Vector2(150, 120);
list.AlternatingRowColors = true;
for (int i = 0; i < 10; i++)
list.AddItem($"Item {i + 1}");
list.OnItemSelected += (lb, idx, item) => Console.WriteLine($"Selected: {item.Text}");
panel.AddChild(list);
4. Run the Update Loop
Stopwatch timer = Stopwatch.StartNew();
float lastTime = 0;
while (running)
{
float currentTime = (float)timer.Elapsed.TotalSeconds;
float deltaTime = currentTime - lastTime;
lastTime = currentTime;
ui.Tick(deltaTime, currentTime);
}
Control Examples
Button Variants
// Standard button
Button btn = new Button { Text = "Normal" };
// Image button (icon only)
Button imgBtn = new Button();
imgBtn.Icon = gfx.LoadImage("icon.png");
imgBtn.IsImageButton = true;
// Toggle button
Button toggleBtn = new Button { Text = "Toggle", IsToggle = true };
// Repeat button (fires while held)
Button repeatBtn = new Button { Text = "Hold Me", IsRepeat = true };
DropDown with Features
// Searchable dropdown
DropDown searchable = new DropDown();
searchable.Searchable = true; // Type to filter
searchable.AddItem("Apple");
searchable.AddItem("Banana");
searchable.AddItem("Cherry");
// Multi-select dropdown
DropDown multi = new DropDown();
multi.MultiSelect = true;
multi.OnMultiSelectionChanged += (dd, indices) => { /* ... */ };
ListBox Features
ListBox list = new ListBox();
list.AlternatingRowColors = true;
list.EvenRowColor = new FishColor(200, 220, 255, 40);
list.MultiSelect = true; // Ctrl+click, Shift+click
// Custom item rendering
list.CustomItemHeight = 28;
list.CustomItemRenderer = (ui, item, index, pos, size, selected, hovered) =>
{
ui.Graphics.DrawRectangle(pos, new Vector2(12, 12), FishColor.Red);
ui.Graphics.DrawText(ui.Settings.FontDefault, item.Text, pos + new Vector2(16, 0));
};
Window with Titlebar
Window window = new Window();
window.Title = "My Window";
window.Position = new Vector2(100, 100);
window.Size = new Vector2(400, 300);
window.ShowCloseButton = true;
window.Resizable = true;
window.OnClosed += (wnd) => wnd.Visible = false;
ui.AddControl(window);
// Add content to window
Label content = new Label("Window content here");
content.Position = new Vector2(10, 10);
window.AddChild(content);
Gauges
// Radial gauge (speedometer style)
RadialGauge radial = new RadialGauge();
radial.Size = new Vector2(150, 150);
radial.MinValue = 0;
radial.MaxValue = 100;
radial.Value = 75;
// Bar gauge (linear)
BarGauge bar = new BarGauge();
bar.Size = new Vector2(200, 30);
bar.MinValue = 0;
bar.MaxValue = 100;
bar.Value = 60;
// VU Meter (audio level)
VUMeter vu = new VUMeter();
vu.Size = new Vector2(30, 100);
vu.Value = 0.7f;
Layout & Positioning
Anchoring
// Anchor to edges (resizes with parent)
Button btn = new Button();
btn.Anchor = FishUIAnchor.Left | FishUIAnchor.Right; // Stretches horizontally
btn.Anchor = FishUIAnchor.All; // Fills parent
Margins
control.Margin = new FishUIMargin(10, 10, 10, 10); // Left, Top, Right, Bottom
StackLayout
StackLayout stack = new StackLayout();
stack.Orientation = StackOrientation.Vertical;
stack.Spacing = 5;
stack.AddChild(new Button { Text = "First" });
stack.AddChild(new Button { Text = "Second" });
stack.AddChild(new Button { Text = "Third" });
Theming
YAML Theme Files
# Theme file example
Atlas: "gwen.png"
Button.Normal:
X: 480
Y: 0
W: 31
H: 31
Left: 8
Right: 8
Top: 8
Bottom: 8
Button.Hovered:
X: 480
Y: 32
W: 31
H: 31
# ...
Color Overrides
// Per-control color customization
label.SetColorOverride("Text", new FishColor(255, 0, 0, 255));
button.SetColorOverride("Text", new FishColor(100, 200, 255, 255));
Opacity
control.Opacity = 0.5f; // 50% transparent (affects children)
Serialization
// Save UI layout
LayoutFormat.SerializeToFile(ui, "layout.yaml");
// Load UI layout
LayoutFormat.DeserializeFromFile(ui, "layout.yaml");
Virtual Cursor (Gamepad/Keyboard Navigation)
// Enable virtual cursor
ui.VirtualMouse.Enabled = true;
ui.VirtualMouse.Speed = 300f;
// In update loop, map gamepad to virtual cursor
if (gamepad.LeftStick.X != 0 || gamepad.LeftStick.Y != 0)
{
ui.VirtualMouse.Move(gamepad.LeftStick * deltaTime);
}
Integrating with Existing Game Loops
When integrating FishUI into an existing game that already handles BeginDrawing()/EndDrawing() calls (e.g., in Raylib), you can disable FishUI's automatic drawing frame management:
// In your graphics backend implementation
public class MyRaylibGfx : IFishUIGfx
{
// Set to false to disable automatic BeginDrawing()/EndDrawing() calls
public bool UseBeginDrawing { get; set; } = false;
public void BeginDrawing(float dt)
{
if (UseBeginDrawing)
{
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.Gray);
}
// Alpha blending is still enabled
Raylib.BeginBlendMode(BlendMode.Alpha);
}
public void EndDrawing()
{
Raylib.EndBlendMode();
if (UseBeginDrawing)
{
Raylib.EndDrawing();
}
}
}
Then in your game loop:
// Your existing game loop
while (!Raylib.WindowShouldClose())
{
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.Black);
// Draw your game content...
DrawGameWorld();
// Draw FishUI on top (it won't call BeginDrawing/EndDrawing)
ui.Tick(deltaTime, currentTime);
Raylib.EndDrawing();
}
This allows FishUI to render as an overlay on top of your existing game rendering without interfering with your drawing frame management.
FishUIEditor - Visual Layout Designer
FishUI includes a visual layout editor for designing interfaces:
cd FishUIEditor
dotnet run
Features:
- Drag-and-drop control placement from toolbox
- Visual resize handles and selection
- PropertyGrid for editing control properties
- Layout hierarchy tree view
- Save/load layouts to YAML files
- Parent/child control relationships with reparenting
- Anchor and Z-ordering support
- Visual feedback for drop targets
- Container selection mode (Window/TabControl protect internal controls)
- Nested control resizing with proper parent offset calculation
Layouts created in the editor can be loaded in your application:
// Load a layout created in FishUIEditor
LayoutFormat.DeserializeFromFile(ui, "data/layouts/my_layout.yaml");
Running Samples
The FishUISample project includes a GUI-based sample chooser:
cd FishUISample
dotnet run
Or run a specific sample:
dotnet run -- --sample 0
Available Samples
- Basic Controls: Textbox, Slider, NumericUpDown, ProgressBar, ToggleSwitch
- Button Variants: Icon buttons, toggle, repeat, image buttons
- DropDown: Basic, searchable, multi-select, custom rendering
- ListBox: Alternating colors, multi-select, custom rendering
- ImageBox: Scale modes, filter modes, animated images
- Gauges: RadialGauge, BarGauge, VUMeter dashboard
- PropertyGrid: Reflection-based property editor
- MenuBar: Dropdown menus with submenus
- ScrollablePane: Virtual scrolling container
- Layout System: Anchoring, margins, StackLayout, FlowLayout, GridLayout
- Theme Switcher: Runtime theme switching
- Virtual Cursor: Keyboard/gamepad navigation
- Game Menu: Example game-style UI
- Editor Layout: Load and display layouts from FishUIEditor
- Data Controls: DataGrid, SpreadsheetGrid, DatePicker, TimePicker
- Serialization: Layout save/load with event handler binding
Documentation
Additional documentation is available in the docs/ folder:
- Custom Control Creation Guide - How to create your own controls
- Theme Creation Guide - Creating custom themes with YAML
Requirements
- .NET 9.0
- YamlDotNet (included via NuGet) - for layout/theme serialization
For the sample application:
- Raylib-cs - graphics/input backend for demos
Project Structure
FishUI/
├── FishUI/ # Core library
│ ├── Controls/ # All UI controls
│ ├── FishUI.cs # Main UI manager
│ ├── FishUISettings.cs # Settings and theme loading
│ ├── IFishUIGfx.cs # Graphics interface
│ ├── IFishUIInput.cs # Input interface
│ └── LayoutFormat.cs # YAML serialization
├── FishUIEditor/ # Visual layout editor
│ ├── Controls/ # Editor-specific controls
│ └── FishUIEditor.cs # Editor application
├── FishUIDemos/ # Sample implementations
│ └── Samples/ # ISample implementations
├── FishUISample/ # Raylib-based runner
│ ├── RaylibGfx.cs # IFishUIGfx implementation
│ ├── RaylibInput.cs # IFishUIInput implementation
│ └── SampleChooser.cs # GUI sample selector
├── docs/ # Documentation
│ ├── CUSTOM_CONTROLS.md # Custom control creation guide
│ └── THEMING.md # Theme creation guide
└── data/ # Assets
├── themes/ # YAML theme files
├── layouts/ # Layout files (editor output)
└── images/ # Sample images
License
MIT License - see repository for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- YamlDotNet (>= 16.3.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on FishUI:
| Package | Downloads |
|---|---|
|
RaylibFishGfx
Raylib graphics and input backend for FishUI. Provides a complete, production-ready implementation using Raylib-cs. |
GitHub repositories
This package is not used by any popular GitHub repositories.