A modern radial menu component for Delphi FMX, designed for smooth touch interaction, elegant UI animations, and integrated busy-state feedback without freezing the interface.
Built for real-world apps (desktop & mobile), production-ready, and now equipped with a custom animated spinner, halo dimming, scrim control, Z-order intelligence, and fluent UX configuration.
Highlights
- Floating circular central button
- Radial animated options
- Smooth in/out animations
- Wires icons via
TPath,TImageList,Bitmap - Can auto-close after option click
- Draggable while open
- Works inside zoom/scroll layouts
- Busy overlay system:
- Dim halo around menu
- Optional input blocking
- Custom-drawn circular spinner (no style dependency)
- Adjustable size, opacity, and color
- Fluent API for configuration
- Production-style comments & UX defaults
Basic Usage
var
Menu: TCircularMenu4D;
Menu :=
TCircularMenu4D.Create(Layout1, 5, TAlphaColorRec.DeepSkyBlue)
.OnCircleCenterClick(procedure begin /* toggle / custom */ end)
.OnCircleClick(0, procedure begin ShowMessage('Option 1'); end)
.OnCircleClick(1, procedure begin ShowMessage('Option 2'); end)
.AutoCloseOnOptionClick(True);
Trigger:
Menu.OpenInteractiveMenu;
Busy Overlay & Spinner Example
Menu
.EnableBusyOverlay(True) // Enables halo + spinner system
.SetBusyOverlayOpacity(0.1) // Subtle dim (0..1)
.BusyBlocksInput(True) // Block UI underneath while loading
.SetBusyScrimColor($55000000) // Halo color (AA RR GG BB)
.SetBusyIndicatorSize(110) // Spinner diameter
.SetBusySpinnerColor(TAlphaColorRec.OrangeRed); // Spinner color
To show/hide busy UI:
Menu.SetBusy(True); // show
Menu.SetBusy(False); // hide
Real async example:
Menu.SetBusy(True);
TTask.Run(
procedure
begin
// Simulates heavy background job
Sleep(2000);
TThread.Synchronize(nil,
procedure
begin
Menu.SetBusy(False);
end);
end);
API Overview
Construction
TCircularMenu4D.Create(AParentLayout, OptionCount [, CenterColor])
Events
.OnCircleCenterClick(TProc)
.OnCircleClick(Index, TProc)
.OnClosed(TProc)
Icons
SetIconForCenter(TPath)
SetIconForOption(Index, TPath)
SetCenterIconFromImageList(...)
SetOptionIconFromImageList(...)
SetCenterIconBitmap(...)
SetOptionIconBitmap(...)
Appearance
SetCenterColor(Color)
SetOptionColor(Index, Color)
SetStroke(Thickness, Color)
SetCenterScale(0.3) // size of center circle
SetGapSpacing(8) // distance between center and options
Busy / Spinner
EnableBusyOverlay(True)
BusyBlocksInput(True)
SetBusyOverlayOpacity(0.1) // halo alpha
SetBusyScrimColor($55000000) // halo RGB
SetBusyIndicatorSize(100) // spinner diameter
SetBusySpinnerColor(Color)
SetBusy(True/False)
Behavior Control
AutoCloseOnOptionClick(True)
AutoDisposeOnClose(True)
HideOnClose(True)
UseDoubleTapToToggle(True)
ManageZOrder(True)
Notes & Best Practices
- If embedding inside a scalable map / zoom UI, the spinner stays center-anchored and readable
- Works well on mobile: taps, double-tap recognition, gestures
- Suggestions:
- Keep option count between 3 and 10 for best UX
- Prefer vector icons (
TPath) for mobile sharpness - Use
BusyBlocksInput(False)for lightweight async work
Developer Tip
To debug layout positioning:
Menu.RecalculateLayout;
And to force reopen after closing animation:
Menu.OpenInteractiveMenu;
Roadmap Ideas
- Haptic feedback on mobile
- Shadow layer & blur halo
- Bounce ease animation
- Built-in icon packs
- FMX-native accessibility roles