https://github.com/sbarisic/fishui
Dependency free, simple GUI
https://github.com/sbarisic/fishui
csharp game-interface gui raylib retained-mode-gui skins
Last synced: 5 months ago
JSON representation
Dependency free, simple GUI
- Host: GitHub
- URL: https://github.com/sbarisic/fishui
- Owner: sbarisic
- License: mit
- Created: 2017-01-10T09:07:25.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2026-01-29T21:07:30.000Z (5 months ago)
- Last Synced: 2026-01-30T03:56:28.838Z (5 months ago)
- Topics: csharp, game-interface, gui, raylib, retained-mode-gui, skins
- Language: C#
- Homepage:
- Size: 74.7 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# FishUI
A dependency-free, immediate-mode-inspired GUI library for .NET applications with backend-agnostic rendering.
[](https://dotnet.microsoft.com/)
[](LICENSE)
[](https://www.nuget.org/packages/FishUI)
[](https://www.nuget.org/packages/RaylibFishGfx)
[](https://deepwiki.com/sbarisic/FishUI)
Uses the [GWEN Skin](https://github.com/benelot/CEGUI-GWEN-Skin) atlas for theming.
## Table of Contents
- [Overview](#overview)
- [Screenshots](#screenshots)
- [Features](#features)
- [Installation](#installation)
- [Projects](#projects)
- [Quick Start](#quick-start)
- [Control Examples](#control-examples)
- [Layout & Positioning](#layout--positioning)
- [Theming](#theming)
- [Serialization](#serialization)
- [Virtual Cursor](#virtual-cursor-gamepadkeyboard-navigation)
- [Integrating with Existing Game Loops](#integrating-with-existing-game-loops)
- [FishUIEditor](#fishuieditor---visual-layout-designer)
- [Running Samples](#running-samples)
- [Documentation](#documentation)
- [Requirements](#requirements)
- [Project Structure](#project-structure)
- [License](#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
## Features
### Controls (50+ 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
## Installation
### NuGet Packages
FishUI is available on NuGet. Choose the package that fits your needs:
#### Option 1: Raylib Backend (Recommended for Quick Start)
If you're using Raylib for graphics/input, install the all-in-one package:
```bash
dotnet add package RaylibFishGfx
```
This includes:
- **FishUI** - Core library with all controls
- **Raylib-cs** - Raylib bindings
- Pre-built `IFishUIGfx` and `IFishUIInput` implementations
#### Option 2: Core Library Only
If you're implementing your own graphics backend:
```bash
dotnet add package FishUI
```
Then implement `IFishUIGfx` and `IFishUIInput` interfaces for your graphics library.
### Data Files
The NuGet packages automatically include theme files, fonts, and icons. These are copied to your output directory on build under the `data/` folder.
## Projects
| Project | Description |
|---------|-------------|
| **FishUI** | Core library - all controls and interfaces |
| **RaylibFishGfx** | Raylib graphics/input backend (NuGet package) |
| **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 two interfaces for your graphics backend (or use the pre-built Raylib backend):
```csharp
// Graphics rendering
public class MyGfx : IFishUIGfx
{
public void Init() { }
public void BeginDrawing(float dt) { }
public void EndDrawing() { }
public int GetWindowWidth() { /* ... */ }
public int GetWindowHeight() { /* ... */ }
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, float size, float spacing, FishColor color) { /* ... */ }
public void BeginScissor(Vector2 pos, Vector2 size) { /* ... */ }
public void EndScissor() { /* ... */ }
// ... 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 FishKey GetKeyPressed() { /* ... */ }
public int GetCharPressed() { /* ... */ }
public float GetMouseWheelMove() { /* ... */ }
// ... see IFishUIInput for full interface
}
// Event handling (optional - can use empty implementation)
public class MyEvents : IFishUIEvents
{
public void Broadcast(FishUI.FishUI ui, Control sender, string eventName, object[] args) { }
}
```
### 2. Initialize FishUI
```csharp
// Using the Raylib backend (recommended)
using RaylibGfx = RaylibFishGfx.RaylibFishGfx;
using RaylibInput = RaylibFishGfx.RaylibInput;
FishUISettings settings = new FishUISettings();
RaylibGfx gfx = new RaylibGfx(800, 600, "My App");
gfx.UseBeginDrawing = false; // Set to false if managing draw calls yourself
IFishUIInput input = new RaylibInput();
IFishUIEvents events = new MyEvents(); // Or use a simple empty implementation
FishUI.FishUI ui = new FishUI.FishUI(settings, gfx, input, events);
ui.Init();
// Load a theme (required for proper rendering)
settings.LoadTheme("data/themes/gwen.yaml", applyImmediately: true);
```
### 3. Add Controls
```csharp
// 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);
check.Size = new Vector2(20, 20); // Size for the checkbox icon
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
```csharp
// Main game loop
while (!Raylib.WindowShouldClose())
{
float dt = Raylib.GetFrameTime();
// Handle window resize
if (Raylib.IsWindowResized())
ui.Resized(Raylib.GetScreenWidth(), Raylib.GetScreenHeight());
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.DarkGray);
// Update and render UI
ui.Tick(dt, (float)Raylib.GetTime());
Raylib.EndDrawing();
}
Raylib.CloseWindow();
```
### Complete Minimal Example
Here's a complete working example using the Raylib backend:
```csharp
using FishUI;
using FishUI.Controls;
using Raylib_cs;
using System.Numerics;
using RaylibGfx = RaylibFishGfx.RaylibFishGfx;
using RaylibInput = RaylibFishGfx.RaylibInput;
// Simple event handler
class SimpleEvents : IFishUIEvents
{
public void Broadcast(FishUI.FishUI ui, Control ctrl, string name, object[] args) { }
}
class Program
{
static void Main()
{
// Setup
var settings = new FishUISettings();
var gfx = new RaylibGfx(800, 600, "FishUI Demo");
gfx.UseBeginDrawing = false;
var ui = new FishUI.FishUI(settings, gfx, new RaylibInput(), new SimpleEvents());
ui.Init();
settings.LoadTheme("data/themes/gwen.yaml", applyImmediately: true);
// Create a button
var button = new Button { Text = "Click Me!", Position = new Vector2(100, 100), Size = new Vector2(120, 40) };
button.OnButtonPressed += (btn, mouse, pos) => Console.WriteLine("Clicked!");
ui.AddControl(button);
// Main loop
while (!Raylib.WindowShouldClose())
{
Raylib.BeginDrawing();
Raylib.ClearBackground(Color.DarkGray);
ui.Tick(Raylib.GetFrameTime(), (float)Raylib.GetTime());
Raylib.EndDrawing();
}
Raylib.CloseWindow();
}
}
```
## Control Examples
### Button Variants
```csharp
// 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
```csharp
// 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
```csharp
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
```csharp
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
```csharp
// 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
```csharp
// 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
```csharp
control.Margin = new FishUIMargin(10, 10, 10, 10); // Left, Top, Right, Bottom
```
### StackLayout
```csharp
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
```yaml
# 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
```csharp
// Per-control color customization
label.SetColorOverride("Text", new FishColor(255, 0, 0, 255));
button.SetColorOverride("Text", new FishColor(100, 200, 255, 255));
```
### Opacity
```csharp
control.Opacity = 0.5f; // 50% transparent (affects children)
```
## Serialization
```csharp
// Save UI layout
LayoutFormat.SerializeToFile(ui, "layout.yaml");
// Load UI layout
LayoutFormat.DeserializeFromFile(ui, "layout.yaml");
```
## Virtual Cursor (Gamepad/Keyboard Navigation)
```csharp
// 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:
```csharp
// 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:
```csharp
// 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:
```bash
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:
```csharp
// 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:
```bash
cd FishUISample
dotnet run
```
Or run a specific sample:
```bash
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](docs/CUSTOM_CONTROLS.md)** - How to create your own controls
- **[Theme Creation Guide](docs/THEMING.md)** - 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 (NuGet: FishUI)
│ ├── Controls/ # All UI controls (50+)
│ ├── FishUI.cs # Main UI manager
│ ├── FishUISettings.cs # Settings and theme loading
│ ├── IFishUIGfx.cs # Graphics interface
│ ├── IFishUIInput.cs # Input interface
│ ├── IFishUIEvents.cs # Event handling interface
│ ├── LayoutFormat.cs # YAML serialization
│ ├── build/ # NuGet build props/targets
│ └── data/ # Themes, fonts, icons
├── RaylibFishUI/ # Raylib backend (NuGet: RaylibFishGfx)
│ ├── RaylibFishGfx.cs # IFishUIGfx implementation
│ └── RaylibInput.cs # IFishUIInput implementation
├── FishUIEditor/ # Visual layout editor
│ ├── Controls/ # Editor-specific controls
│ └── FishUIEditor.cs # Editor application
├── FishUIDemos/ # Sample implementations
│ ├── Samples/ # ISample implementations
│ └── Forms/ # Designer form examples
├── FishUISample/ # Raylib-based sample runner
│ ├── Program.cs # Sample chooser entry point
│ └── SampleChooser.cs # GUI sample selector
├── NugetTest/ # NuGet package testing project
├── docs/ # Documentation
│ ├── CUSTOM_CONTROLS.md # Custom control creation guide
│ └── THEMING.md # Theme creation guide
└── screenshots/ # Screenshot gallery
```
## License
MIT License - see repository for details.