TheCodeNaked

CircularMenu4D (Copy)


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 TPathTImageListBitmap
  • 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
Sobre o autor

TheCodeNaked

No TheCodeNaked, programar é consequência, não ponto de partida. Antes do código, vem a dúvida, a análise, o contexto. Não seguimos fórmulas — questionamos. Criar software é pensar com clareza. O resto é só digitação.

TheCodeNaked

Criar com clareza. Codificar com intenção.

TheCodeNaked

Ótimo! Você se inscreveu com sucesso.

Bem-vindo de volta! Você acessou com sucesso.

Você se inscreveu com sucesso o TheCodeNaked.

Sucesso! Verifique seu e-mail para acessar com o link mágico.

As suas informações de faturamento foram atualizadas.

Seu pagamento não foi atualizado