Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ryzhpolsos/redeye/llms.txt

Use this file to discover all available pages before exploring further.

Plugins can register new widget types that RWML layout files can use just like built-in widgets. A custom widget is a C# class that implements IShellWidget — typically by extending the BaseShellWidget abstract base, which handles positioning, styling, tooltip wiring, and thread-safe control updates for you. Once exported, users reference your widget in RWML using the pluginId.widgetName element name.

Creating a custom widget

1

Create a class that extends BaseShellWidget

Subclass RedEye.UI.BaseShellWidget. You must assign Control to a WinForms control in Initialize() before the base infrastructure can operate on it.
using System.Windows.Forms;
using RedEye.UI;
using RedEye.Core;

namespace MyPlugin {
    public class ClockWidget : BaseShellWidget {
        private Label label;

        public override void Initialize() {
            label = new Label {
                TextAlign = System.Drawing.ContentAlignment.MiddleCenter
            };
            Control = label;

            // Call base AFTER assigning Control — it sets up tooltips and the update loop
            base.Initialize();
        }
    }
}
BaseShellWidget inherits from IShellWidget and also implements IComponent. You never need to implement SetManager, GetControl, SetConfig, GetConfig, Update, Modify, or the event infrastructure yourself.
2

Override UpdateConfig() to read RWML attributes

UpdateConfig() is called before every render cycle. Call base.UpdateConfig() first to populate the standard ShellWidgetConfig fields (position, size, colors, font, etc.), then read any additional attributes specific to your widget from Node.
public override void UpdateConfig() {
    base.UpdateConfig();

    // Read a custom attribute from the RWML element
    var format = Node.GetAttribute("format", "HH:mm:ss");
    Config.Font = string.IsNullOrEmpty(Config.Font) ? "Segoe UI, 10" : Config.Font;
}
3

Override UpdateControl() to update the WinForms control

UpdateControl() is called after UpdateConfig() to push the current state into the control. Call the base implementation first so that standard layout properties (position, size, colors, font, dock, anchor) are applied, then update your control’s specific state.
public override void UpdateControl() {
    base.UpdateControl();

    var format = Node.GetAttribute("format", "HH:mm:ss");
    label.Text = DateTime.Now.ToString(format);
}
For widgets that update on a timer, set updateInterval in the RWML element attribute (milliseconds). The base class starts the polling loop automatically when Config.UpdateInterval > 0.
4

Override PostInitialize() for deferred setup (optional)

PostInitialize() runs after all widgets in the layout have been initialised. Use it when your widget needs to reference another widget by ID (via Config.Parent) or react to the fully-built widget tree. The base implementation wires up the Parent control relationship and tooltip mouse events.
public override void PostInitialize() {
    base.PostInitialize();

    // Example: find a sibling widget and subscribe to its events
    // Window.GetWidget("some-id")?.RegisterEventHandler("Click", handler);
}
5

Call ExportWidget in your plugin's Main()

From your Plugin subclass, call ExportWidget<T>(name) to register the widget type. The full name in RWML will be pluginId.name.
using RedEye.PluginAPI;

namespace MyPlugin {
    public class MyPlugin : Plugin {
        public override void Main() {
            ExportWidget<ClockWidget>("clock");
        }
    }
}
6

Use the widget in an RWML layout file

Reference the exported widget using the pluginId.widgetName element. All standard widget attributes (x, y, width, height, color, backgroundColor, font, dock, updateInterval, etc.) are supported because BaseShellWidget reads them in UpdateConfig().
<my-plugin.clock
    x="200"
    y="4"
    width="120"
    height="22"
    format="HH:mm"
    color="#ffffff"
    font="Segoe UI, 11"
    updateInterval="1000" />

ShellWidgetConfig — standard widget fields

BaseShellWidget.UpdateConfig() maps RWML attributes to a ShellWidgetConfig instance stored in Config. These fields are available to every widget that calls base.UpdateConfig():
FieldRWML attributeTypeDescription
IdidstringWidget identifier for cross-widget references. Auto-generated if omitted.
XxintLeft position in pixels.
YyintTop position in pixels.
WidthwidthintWidth in pixels.
HeightheightintHeight in pixels.
AutoSizeautoSizeboolLet the control size itself.
DockdockstringWinForms DockStyle value (e.g. fill, top).
AnchoranchorstringWinForms AnchorStyles value.
ColorcolorstringForeground color as a hex string.
BackgroundColorbackgroundColorstringBackground color as a hex string.
PaddingpaddingstringControl padding.
MarginmarginstringControl margin.
FontfontstringFont string (e.g. "Segoe UI, 11").
UpdateIntervalupdateIntervalintPolling interval in milliseconds. 0 disables the timer.
ToolTiptoolTipstringTooltip text shown on hover.
LayerlayerintZ-order layer. Defaults to -1.
IsTransparentisTransparentboolRenders the control background as transparent.
ParentparentstringID of a parent widget to nest this control inside.

Complete example — clock widget

using System;
using System.Drawing;
using System.Windows.Forms;
using RedEye.UI;
using RedEye.PluginAPI;

namespace ClockPlugin {
    public class ClockWidget : BaseShellWidget {
        private Label label;

        public override void Initialize() {
            label = new Label {
                TextAlign = ContentAlignment.MiddleCenter,
                ForeColor = Color.White
            };
            Control = label;
            base.Initialize();
        }

        public override void UpdateConfig() {
            base.UpdateConfig();
        }

        public override void UpdateControl() {
            base.UpdateControl();
            var format = Node.GetAttribute("format", "HH:mm:ss");
            label.Text = DateTime.Now.ToString(format);
        }
    }

    public class ClockPlugin : Plugin {
        public override void Main() {
            ExportWidget<ClockWidget>("clock");
        }
    }
}
Usage in RWML:
<clock-plugin.clock
    x="0" y="0"
    width="150" height="24"
    format="HH:mm"
    updateInterval="5000"
    color="#cccccc" />

Implementing IShellWidget directly

If BaseShellWidget does not fit your use case (for example, you are wrapping a non-WinForms UI framework or need full control over every lifecycle call), implement IShellWidget directly. You must implement all interface members: Initialize, PostInitialize, GetNode, SetNode, GetConfig, SetConfig, UpdateConfig, GetContainer, SetContainer, GetWindow, SetWindow, GetControl, UpdateControl, Update, Modify, and RegisterEventHandler. You also need to implement IComponent.SetManager and IComponent.Initialize (from the parent IComponent interface). See IShellWidget API for the full interface contract.

Build docs developers (and LLMs) love