ktsu.ImGui.Widgets 3.8.1

Prefix Reserved
dotnet add package ktsu.ImGui.Widgets --version 3.8.1
                    
NuGet\Install-Package ktsu.ImGui.Widgets -Version 3.8.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ktsu.ImGui.Widgets" Version="3.8.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ktsu.ImGui.Widgets" Version="3.8.1" />
                    
Directory.Packages.props
<PackageReference Include="ktsu.ImGui.Widgets" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ktsu.ImGui.Widgets --version 3.8.1
                    
#r "nuget: ktsu.ImGui.Widgets, 3.8.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ktsu.ImGui.Widgets@3.8.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ktsu.ImGui.Widgets&version=3.8.1
                    
Install as a Cake Addin
#tool nuget:?package=ktsu.ImGui.Widgets&version=3.8.1
                    
Install as a Cake Tool

ktsu.ImGui.Widgets

ImGuiWidgets is a library of custom widgets using ImGui.NET. This library provides a variety of widgets and utilities to enhance your ImGui-based applications.

Features

  • Knobs: Ported to .NET from ImGui-works/ImGui-knobs-dial-gauge-meter
  • Radial Progress Bar: Circular progress indicators for visualizing loading and progress with countdown/count-up timers
  • Resizable Layout Dividers: Draggable layout dividers for resizable layouts (DividerContainer)
  • TabPanel: Tabbed interface with closable, reorderable tabs and dirty indicator support
  • Combo: Type-safe combo boxes for enums, strings, and strong strings
  • Icons: Customizable icons with various alignment options and event delegates
  • Grid: Flexible grid layout for displaying items
  • Color Indicator: An indicator that displays a color when enabled
  • Image: An image widget with alignment options
  • Text: A text widget with alignment options
  • Tree: A tree widget for displaying hierarchical data
  • Scoped Id: A utility class for creating scoped IDs
  • Scoped Disable: Temporarily disable UI elements within a scope
  • SearchBox: A powerful search box with support for various filter types (Glob, Regex, Fuzzy) and matching options
  • Hexa-backed widgets: Thin adapters over Hexa.NET.ImGui.Widgets — spinners, buffering bars, splitters, toggle/transparent/inline buttons, an icon tree node, an enum combo, text/image alignment helpers, tooltips, breadcrumbs, a date/year picker, a flame graph, a file tree view, stateful file/rename/message dialogs, and a docked-window base class. See Hexa-backed Widgets below.
  • Callback-driven editors: Sequencer (an editable clip timeline), CurveEditor (a multi-curve graph, or a single CurveData curve), and BezierEditor (a cubic easing curve) — driven by a SequenceSource/CurveSource you subclass, or by a CurveData/BezierControlPoints value. See Callback-driven Editors below.

Installation

To install ImGuiWidgets, you can add the library to your .NET project using the following command:

dotnet add package ktsu.ImGui.Widgets

Usage

To use ImGuiWidgets, you need to include the ktsu.ImGui.Widgets namespace in your code:

using ktsu.ImGui.Widgets;

Then, you can start using the widgets provided by ImGuiWidgets in your ImGui-based applications.

Examples

Here are some examples of using ImGuiWidgets:

Knobs

Knobs are useful for creating dial-like controls:

float value = 0.5f;
float minValue = 0.0f;
float maxValue = 1.0f;

ImGuiWidgets.Knob("Knob", ref value, minValue, maxValue);

Radial Progress Bar

The RadialProgressBar widget displays circular progress indicators perfect for loading states, progress tracking, countdowns, and timers:

float progress = 0.65f; // Progress from 0.0 to 1.0

// Basic usage - displays with default size and settings (clockwise from top, percentage text)
ImGuiWidgets.RadialProgressBar(progress);

// Custom size (radius in pixels)
ImGuiWidgets.RadialProgressBar(progress, radius: 50);

// Custom thickness
ImGuiWidgets.RadialProgressBar(progress, radius: 50, thickness: 10);

// Without text in center
ImGuiWidgets.RadialProgressBar(progress, 50, 0, 32, ImGuiRadialProgressBarOptions.NoText);

// Counter-clockwise direction (default is clockwise)
ImGuiWidgets.RadialProgressBar(progress, 50, 0, 32, ImGuiRadialProgressBarOptions.CounterClockwise);

// Start at bottom instead of top
ImGuiWidgets.RadialProgressBar(progress, 50, 0, 32, ImGuiRadialProgressBarOptions.StartAtBottom);

// Combine options: counter-clockwise starting at bottom
ImGuiWidgets.RadialProgressBar(progress, 50, 0, 32,
    ImGuiRadialProgressBarOptions.CounterClockwise | ImGuiRadialProgressBarOptions.StartAtBottom);

// Animated progress example
float animatedProgress = 0.0f;
void UpdateProgress(float deltaTime)
{
    animatedProgress += deltaTime * 0.2f;
    if (animatedProgress > 1.0f) animatedProgress = 0.0f;

    ImGuiWidgets.RadialProgressBar(animatedProgress);
}
Text Display Modes

The RadialProgressBar supports three text display modes:

// Percentage mode (default) - displays "65%"
ImGuiWidgets.RadialProgressBar(0.65f);

// Time mode - displays time in MM:SS or HH:MM:SS format
ImGuiWidgets.RadialProgressBar(
    progress: 0.5f,
    textMode: ImGuiRadialProgressBarTextMode.Time,
    timeValue: 150.0f  // Displays "02:30"
);

// Time mode with hours - displays "01:05:30"
ImGuiWidgets.RadialProgressBar(
    progress: 0.7f,
    textMode: ImGuiRadialProgressBarTextMode.Time,
    timeValue: 3930.0f  // 1 hour, 5 minutes, 30 seconds
);

// Custom text mode - displays any string you provide
ImGuiWidgets.RadialProgressBar(
    progress: 0.3f,
    textMode: ImGuiRadialProgressBarTextMode.Custom,
    customText: "Loading..."
);
Countdown Timer

Use RadialCountdown for countdown timers showing time remaining:

float countdownTime = 300.0f;  // 5 minutes in seconds
const float CountdownTotal = 300.0f;
bool isRunning = false;

// Update countdown
if (isRunning && countdownTime > 0.0f)
{
    countdownTime -= deltaTime;
    if (countdownTime < 0.0f)
    {
        countdownTime = 0.0f;
        isRunning = false;
    }
}

// Display countdown - shows time remaining (e.g., "05:00", "04:30", etc.)
ImGuiWidgets.RadialCountdown(countdownTime, CountdownTotal);

// With custom options
ImGuiWidgets.RadialCountdown(
    countdownTime,
    CountdownTotal,
    radius: 60,
    thickness: 12,
    segments: 64,
    options: ImGuiRadialProgressBarOptions.CounterClockwise
);

// Reset button
if (ImGui.Button("Reset"))
{
    countdownTime = CountdownTotal;
    isRunning = false;
}
Count-Up Timer

Use RadialCountUp for timers showing elapsed time:

float elapsedTime = 0.0f;
const float TotalTime = 180.0f;  // 3 minutes
bool isRunning = false;

// Update timer
if (isRunning && elapsedTime < TotalTime)
{
    elapsedTime += deltaTime;
    if (elapsedTime > TotalTime)
    {
        elapsedTime = TotalTime;
        isRunning = false;
    }
}

// Display count-up timer - shows elapsed time (e.g., "00:00", "00:15", etc.)
ImGuiWidgets.RadialCountUp(elapsedTime, TotalTime);

// With custom size and options
ImGuiWidgets.RadialCountUp(
    elapsedTime,
    TotalTime,
    radius: 70,
    options: ImGuiRadialProgressBarOptions.StartAtBottom
);

// Start/stop controls
if (ImGui.Button(isRunning ? "Stop" : "Start"))
{
    isRunning = !isRunning;
}

if (ImGui.Button("Reset"))
{
    elapsedTime = 0.0f;
    isRunning = false;
}
Advanced Timer Examples
// Pomodoro timer (25 minutes work, 5 minutes break)
const float WorkDuration = 1500.0f;  // 25 minutes
const float BreakDuration = 300.0f;  // 5 minutes
float currentTime = WorkDuration;
bool isWorkSession = true;

if (isWorkSession)
{
    ImGuiWidgets.RadialCountdown(currentTime, WorkDuration, radius: 80);
}
else
{
    ImGuiWidgets.RadialCountdown(currentTime, BreakDuration, radius: 80);
}

// Stopwatch with custom display
float stopwatchTime = 0.0f;
ImGuiWidgets.RadialProgressBar(
    progress: 0.0f,  // No progress bar fill
    radius: 60,
    textMode: ImGuiRadialProgressBarTextMode.Time,
    timeValue: stopwatchTime,
    options: ImGuiRadialProgressBarOptions.NoText  // Hide text if desired
);
ImGui.Text($"Elapsed: {stopwatchTime:F2}s");

// Combined progress and time display
float taskProgress = 0.35f;
float taskTimeRemaining = 120.0f;  // 2 minutes remaining

ImGui.Columns(2);
ImGuiWidgets.RadialProgressBar(taskProgress);
ImGui.TextUnformatted("Progress");
ImGui.NextColumn();

ImGuiWidgets.RadialProgressBar(
    taskProgress,
    textMode: ImGuiRadialProgressBarTextMode.Time,
    timeValue: taskTimeRemaining
);
ImGui.TextUnformatted("Time Remaining");
ImGui.Columns(1);

The SearchBox widget provides a powerful search interface with multiple filter type options:

// Static fields to maintain filter state between renders.
// The options record carries the label, filter type, match options,
// and hint/tooltip/context-menu toggles. Right-click updates it in place.
private static string searchTerm = string.Empty;
private static SearchBoxOptions searchOptions = new(Label: "##BasicSearch", FilterType: TextFilterType.Glob);
private static SearchBoxRankedOptions rankedOptions = new(Label: "##RankedSearch");

// List of items to search
var items = new List<string> { "Apple", "Banana", "Cherry", "Date", "Elderberry" };

// Basic search box with right-click context menu for filter options
ImGuiWidgets.SearchBox(ref searchOptions, ref searchTerm);

// Display results
if (!string.IsNullOrEmpty(searchTerm))
{
    ImGui.TextUnformatted($"Search results for: {searchTerm}");
}

// Search box that returns filtered results directly
var filteredResults = ImGuiWidgets.SearchBox(
    ref searchOptions,
    ref searchTerm,
    items,                  // Collection to filter
    item => item).ToList();  // Selector function to extract string from each item

// Ranked search box for fuzzy matching and ranked results
var rankedResults = ImGuiWidgets.SearchBoxRanked(
    ref rankedOptions,
    ref searchTerm,
    items,
    item => item).ToList();

TabPanel

TabPanel creates a tabbed interface with support for closable tabs, reordering, and dirty state indication:

// Create a tab panel with closable and reorderable tabs
var tabPanel = new ImGuiWidgets.TabPanel("MyTabPanel", true, true);

// Add tabs with explicit IDs (recommended for stability when tabs are reordered)
string tab1Id = tabPanel.AddTab("tab1", "First Tab", RenderTab1Content);
string tab2Id = tabPanel.AddTab("tab2", "Second Tab", RenderTab2Content);
string tab3Id = tabPanel.AddTab("tab3", "Third Tab", RenderTab3Content);

// Draw the tab panel in your render loop
tabPanel.Draw();

// Methods to render tab content
void RenderTab1Content()
{
    ImGui.Text("Tab 1 Content");

    // Mark tab as dirty when content changes
    if (ImGui.Button("Edit"))
    {
        tabPanel.MarkTabDirty(tab1Id);
    }

    // Mark tab as clean when content is saved
    if (ImGui.Button("Save"))
    {
        tabPanel.MarkTabClean(tab1Id);
    }
}

void RenderTab2Content()
{
    ImGui.Text("Tab 2 Content");
}

void RenderTab3Content()
{
    ImGui.Text("Tab 3 Content");
}

Icons

Icons can be used to display images with various alignment options and event delegates:

float iconWidthEms = 7.5f;
float iconWidthPx = ImGuiApp.EmsToPx(iconWidthEms);

// GetOrLoadTexture returns an ImGuiAppTextureInfo; use its TextureId
ImGuiAppTextureInfo texture = ImGuiApp.GetOrLoadTexture("icon.png");

ImGuiWidgets.Icon("Click Me", texture.TextureId, iconWidthPx, ImGuiWidgets.IconAlignment.Vertical, new ImGuiWidgets.IconOptions()
{
    OnClick = () => Console.WriteLine("You clicked")
});

ImGui.SameLine();
ImGuiWidgets.Icon("Double Click Me", texture.TextureId, iconWidthPx, ImGuiWidgets.IconAlignment.Vertical, new ImGuiWidgets.IconOptions()
{
    OnDoubleClick = () => Console.WriteLine("You clicked twice")
});

ImGui.SameLine();
ImGuiWidgets.Icon("Right Click Me", texture.TextureId, iconWidthPx, ImGuiWidgets.IconAlignment.Vertical, new ImGuiWidgets.IconOptions()
{
    OnContextMenu = () =>
    {
        ImGui.MenuItem("Context Menu Item 1");
        ImGui.MenuItem("Context Menu Item 2");
        ImGui.MenuItem("Context Menu Item 3");
    },
});

Grid

The grid layout allows you to display items in a flexible grid:

float iconSizeEms = 7.5f;
float iconSizePx = ImGuiApp.EmsToPx(iconSizeEms);

ImGuiAppTextureInfo texture = ImGuiApp.GetOrLoadTexture("icon.png");

// RowMajorGrid (or ColumnMajorGrid) takes an id, the items, a measure delegate, and a draw delegate
ImGuiWidgets.RowMajorGrid(
    "MyGrid",
    items,
    item => ImGuiWidgets.CalcIconSize(item, iconSizePx, ImGuiWidgets.IconAlignment.Vertical),
    (item, cellSize, itemSize) =>
    {
        ImGuiWidgets.Icon(item, texture.TextureId, iconSizePx, ImGuiWidgets.IconAlignment.Vertical);
    });

Color Indicator

The color indicator widget displays a color when enabled:

// color is a Hexa.NET.ImGui.ImColor (for example, from ktsu.ImGui.Styler's Color helpers)
ImColor color = Color.FromHex("#ff0000");
bool enabled = true;

ImGuiWidgets.ColorIndicator(color, enabled);

Image

The image widget allows you to display images with alignment options:

ImGuiAppTextureInfo texture = ImGuiApp.GetOrLoadTexture("image.png");

ImGuiWidgets.Image(texture.TextureId, new Vector2(100, 100));

Text

The text widget allows you to display text with alignment options:

ImGuiWidgets.Text("Hello, ImGuiWidgets!");
ImGuiWidgets.TextCentered("Hello, ImGuiWidgets!");
ImGuiWidgets.TextCenteredWithin("Hello, ImGuiWidgets!", new Vector2(100, 100));

Tree

The tree widget allows you to display hierarchical data:

using (var tree = new ImGuiWidgets.Tree())
{
    for (int i = 0; i < 5; i++)
    {
        using (tree.Child)
        {
            ImGui.Button($"Hello, Child {i}!");
            using (var subtree = new ImGuiWidgets.Tree())
            {
                using (subtree.Child)
                {
                    ImGui.Button($"Hello, Grandchild!");
                }
            }
        }
    }
}

Scoped Id

The scoped ID utility class helps in creating scoped IDs for ImGui elements and ensuring they get popped appropriately:

using (new ImGuiWidgets.ScopedId())
{
    ImGui.Button("Hello, Scoped ID!");
}

Scoped Disable

Temporarily disable UI elements within a scope. Disabled elements are visually grayed out and non-interactive:

bool shouldDisable = true;

// Disable buttons within this scope
using (new ScopedDisable(shouldDisable))
{
    ImGui.Button("I'm disabled!");
    ImGui.InputText("Disabled Input", ref someText, 256);
}

// Elements outside the scope are enabled normally
ImGui.Button("I'm enabled!");

// Nested disables work as expected (per Dear ImGui rules)
using (new ScopedDisable(false))
{
    ImGui.Text("Enabled section");

    using (new ScopedDisable(true))
    {
        ImGui.Button("Disabled button");
    }
}

Note: As per Dear ImGui documentation, nested BeginDisabled calls cannot re-enable an already disabled section - a single BeginDisabled(true) in the stack is enough to keep everything disabled.

Combo

Type-safe combo box widgets for enums, strings, and strong strings:

// Enum combo box
enum Season { Spring, Summer, Fall, Winter }
Season selectedSeason = Season.Summer;

if (ImGuiWidgets.Combo("Season", ref selectedSeason))
{
    Console.WriteLine($"Selected: {selectedSeason}");
}

// String combo box
string selectedFruit = "Apple";
var fruits = new Collection<string> { "Apple", "Banana", "Cherry", "Date" };

if (ImGuiWidgets.Combo("Fruit", ref selectedFruit, fruits))
{
    Console.WriteLine($"Selected: {selectedFruit}");
}

// Strong string combo box (using ktsu.Semantics.Strings)
using ktsu.Semantics.Strings;

MyStrongString selected = new("Value1");
var options = new Collection<MyStrongString>
{
    new("Value1"),
    new("Value2"),
    new("Value3")
};

if (ImGuiWidgets.Combo("Option", ref selected, options))
{
    Console.WriteLine($"Selected: {selected}");
}

DividerContainer

Create resizable layouts with draggable dividers between content regions:

// Create a column-based divider container (side-by-side zones)
var dividerContainer = new ImGuiWidgets.DividerContainer(
    "MyContainer",
    ImGuiWidgets.DividerLayout.Columns
);

// Add zones with: id, initial size (relative weight), resizable, and a tick delegate (receives delta time)
dividerContainer.Add("Left Panel", 0.33f, true, dt =>
{
    ImGui.Text("Left side content");
    ImGui.Button("Left Button");
});

dividerContainer.Add("Right Panel", 0.67f, true, dt =>
{
    ImGui.Text("Right side content");
    ImGui.Button("Right Button");
});

// Tick the container each frame in your render loop
dividerContainer.Tick(deltaTime);

// For a stacked (top/bottom) layout, use DividerLayout.Rows:
var stackedContainer = new ImGuiWidgets.DividerContainer(
    "StackedContainer",
    ImGuiWidgets.DividerLayout.Rows
);

stackedContainer.Add("Top Panel", 0.5f, true, dt =>
{
    ImGui.Text("Top content");
});

stackedContainer.Add("Bottom Panel", 0.5f, true, dt =>
{
    ImGui.Text("Bottom content");
});

stackedContainer.Tick(deltaTime);

The dividers can be dragged by the user to resize the content regions dynamically.

Hexa-backed Widgets

These widgets are thin adapters that delegate to Hexa.NET.ImGui.Widgets rather than reimplementing rendering logic:

  • Spinner: Indeterminate loading spinner animated from the ImGui frame time
  • BufferingBar: Horizontal bar filled left-to-right in proportion to a value
  • HorizontalSplitter / VerticalSplitter: Draggable splitters that adjust a bound height/width within min/max limits
  • ToggleSwitch: Sliding on/off switch
  • ToggleButton: Button that shows a highlight ring while selected
  • TransparentButton: Button with no background until hovered
  • InlineButton: Compact button anchored inside an existing rectangle, for rows and headers
  • IconTreeNode: Tree node with a coloured icon glyph before its label
  • EnumCombo<T>: Combo box listing every member of an enum type
  • TextCenteredV / TextCenteredH / TextCenteredVH: Text centred vertically, horizontally, or both
  • ImageCenteredV / ImageCenteredH / ImageCenteredVH: Image centred vertically, horizontally, or both
  • ImageScaleTo: Image scaled to fit inside a destination box while preserving aspect ratio
  • Tooltip: Shows a tooltip for the preceding item while it is hovered
  • Breadcrumb: Clickable breadcrumb trail from a separator-delimited path
  • DatePicker: Calendar control for picking a date
  • YearPicker: Grid control for picking a year
  • FlameGraph: Flame graph of hierarchical timing samples
  • FileTreeView: Navigable tree of the filesystem rooted at the machine's drives
  • OpenFileDialog / SaveFileDialog / OpenFolderDialog: Stateful dialogs for choosing existing files, a save destination, or a folder
  • RenameDialog: Renames or moves a file, reporting success or failure without throwing
  • DialogMessageBox / ShowMessageBox: A movable-window-style and a popup-style message box, respectively
  • DockedWindow: Abstract base for a floating window the user can drag into the dockspace DrawDeferredDocked() creates — subclass it, override Title and DrawContent(), then call Show()/Close(). It is dockable, not auto-docked: it opens floating and stays there until the user drags it in

Material Icons font: DatePicker (Material CalendarToday, U+E935) and FileTreeView (Home U+E9B2, Computer U+E31E) render placeholder boxes unless a Material Icons font is registered in the atlas. OpenFileDialog, SaveFileDialog and OpenFolderDialog need the same font for their toolbar, breadcrumb and file-tree glyphs. Register it via FontHelper.AddCustomFont(io, fontData, size, FontHelper.GetMaterialIconRanges(), mergeWithPrevious: true) — not via ImGuiAppConfig.Fonts, which applies the Nerd Font mapping and leaves the glyphs unmapped. See examples/ImGuiAppDemo for a worked example. YearPicker, RenameDialog, DialogMessageBox and ShowMessageBox require no icon font.

Duplicate widgets: Several Hexa-backed widgets deliberately coexist with an existing ktsu widget that covers similar ground: HorizontalSplitter/VerticalSplitter vs DividerContainer, IconTreeNode vs Tree, ToggleSwitch vs Switch, BufferingBar/Spinner vs RadialProgressBar/SkeletonLoader, EnumCombo vs Combo, TextCenteredV/H/VH vs TextCentered, and ImageCenteredV/H/VH vs ImageCentered. Both sides of each pair remain until the "Hexa vs ktsu" comparison tab in examples/ImGuiWidgetsDemo settles which one to keep — that decision is a separate, breaking change.

Deferred drawing: The dialogs above and DockedWindow only draw when a per-frame pump runs. Call ImGuiWidgets.DrawDeferred() once per frame (at the end of OnRender) to draw every open dialog, message box and popup and advance Hexa's animation clock; call ImGuiWidgets.DrawDeferredDocked() instead if you use DockedWindow — it additionally enables ImGuiConfigFlags.DockingEnable (idempotently, since Hexa's dockspace is a no-op without it) and creates a dockspace over the main viewport, and it already does everything DrawDeferred() does, so call only one of the two per frame (calling both draws every dialog twice). Showing a dialog before either pump has ever run throws InvalidOperationException, as does calling Show() on a dialog instance that is already shown (Hexa would register the same instance twice and permanently block input) — wait for the close callback, or create a new instance per showing. A pump is not needed just to keep animated widgets like ToggleSwitch correct — it self-ticks when unpumped — only to show dialogs or docked windows.

Callback-driven Editors

Sequencer and the multi-curve CurveEditor overload take a source object they interrogate while drawing, instead of a value:

  • Subclass SequenceSource for a timeline: FrameMin, FrameMax, ItemCount, GetItem(int), and SetItemRange(int index, int start, int endFrame), which receives drag edits.
  • Subclass CurveSource for a multi-curve graph: CurveCount, ViewMin, ViewMax, GetPointCount(int), GetPoints(int), GetCurveColor(int), EditPoint(int, int, Vector2), AddPoint(int, Vector2).

Neither needs a deferred-drawing pump — both are immediate-mode calls that happen to take a callback object, and neither Sequencer nor CurveEditor calls DrawDeferred()/DrawDeferredDocked().

public static bool ImGuiWidgets.Sequencer(SequenceSource source, ref int currentFrame, ref bool expanded,
    ref int selectedEntry, ref int firstFrame, SequencerFeatures features = SequencerFeatures.EditAll);

public static bool ImGuiWidgets.CurveEditor(CurveSource source, Vector2 size, string id);

CurveEditor also has a single-curve overload taking a CurveData value instead of a CurveSource:

public static bool ImGuiWidgets.CurveEditor(CurveData curve, Vector2 size, Vector2 rangeMin,
    Vector2 rangeMax, ref int selection, string label);

CurveData wraps the curve representation the widget expects — points are CurveKnot (Position plus a CurvePointKind of .Smooth or .Corner), shaped by CurveShape.Smooth/.Freehand — and tracks a dirty flag so Sample(float t) recomputes its cache automatically after AddPoint/SetPoint/RemovePoint/Clear, a Shape change, or an edit made through the widget.

BezierEditor edits a BezierControlPoints pair (First/Second) directly, with no source object:

public static bool ImGuiWidgets.BezierEditor(string label, ref BezierControlPoints points, float size = 128f);

Contributing

Contributions are welcome! For feature requests, bug reports, or questions, please open an issue on the GitHub repository. If you would like to contribute code, please open a pull request with your changes.

Acknowledgements

ImGuiWidgets is inspired by the following projects:

License

ImGuiWidgets is licensed under the MIT License. See LICENSE for more information.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
3.8.1 0 8/19/2026
3.8.0 0 8/19/2026
3.7.3 0 8/19/2026
3.7.2 0 8/18/2026
3.7.1 67 8/17/2026
3.7.0 81 8/17/2026
3.6.1 81 8/16/2026
3.6.0 54 8/16/2026
3.5.2 57 8/16/2026
3.5.1 60 8/16/2026
3.5.0 66 8/15/2026
3.4.3 56 8/15/2026
3.4.2 48 8/15/2026
3.4.1 60 8/15/2026
3.4.0 59 8/15/2026
3.3.12 141 8/12/2026
3.3.11 95 8/11/2026
3.3.10 95 8/11/2026
3.3.9 119 8/6/2026
Loading failed

## v3.8.1 (patch)

Changes since v3.8.0:

- chore: refresh the API compatibility suppressions [patch] ([@matt-edmondson](https://github.com/matt-edmondson))