Skip to content
View RetroFuturaGUI's full-sized avatar

Block or report RetroFuturaGUI

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RetroFuturaGUI/README.md

RetroFuturaGUI

demo

RetroFuturaGUI is a signal-slot-based GUI framework written in C++. It merges retro-inspired and modern aesthetics with backward/forward compatibility - hence its name. The framework is designed for cross-platform use, and its logic can be compiled into a dynamic library, making it accessible from other languages while ensuring compatibility with past, current, and future standards.

Roadmap

# Name Depends on Current State
1 Button, Label, Window, MainWindow, Image, Grid2D, WindowBar with Buttons ✅
2 dll/so/dylib compilation for C# and Python support, Widget ID manager 1 ✅
3 Linux Support, Font Manager 2 ✅
4 More Widgets (TextBox ✅, Table ✅, 3D Model ✅, Slider ✅, CheckBox ✅, ComboBox ✅, ExtendedComoBox ✅, RadioButton ✅, RadioButtonGroup ✅, SeparatorLine ✅, ColorPreview ✅, change Grid2d to "Lasagna" and add a 3rd dimension ✅, MenuBar ✅, MultilineTextBox, Environment, Histogram ✅, LineDiagram, Tabs, Lights, VideoPlayer ✅, Color Pickers), Scene ✅, SceneLoader ✅, AudioPlayback ✅, Popups, PopupManager, Tooltip, FileExplorer 1 WIP
5 .bechaml markup language for GUI design 🥣 (Beautifully Extended Cascading but Hairbally Application Markup Language) 4
6 VS Code extension with project generator/manager 5
7 Pre-built Prefabs (StepperSlider, SpinBox, Table with Sliders, Carousel, Extended Color Pickers, MediaPlayer) 6
8 Interoperability API Exporter 7
9 Aesthetics: Flat Metro, Frutiger Aero (Win Vista/7, Wii, Wii U, Pikmin 3), Frutiger Metro, Y2K Futurism (F-Zero GX), Vaporwave, Outrun/Synthwave, Neumorphism, Glassmorphism/Liquid Glass, Constructivism, Comic/Anime/LoFi, Bauhaus, ... 8
10 Android (without Android Studio), MacOS, iOS support 8
11 dll/so/dylib compilation for C, Rust, Zig, Java, JavaScript, TypeScript, Go, Ruby, Lua, Swift, Carbon, Gleam 8
12 gcn and wii support (libogc (devkitPro)) 8
13 XBox series Support (all XBox consoles, not just the Series series, du'h) (DirectX), DirectX for Windows 8
14 Dreamcast support (OpenGL 1.5 + PVR APi) 8
15 Wii U support (GX2 (devkitPro)) 8
16 3DS support (citro3d/citro2d (devkitPro)) 8
17 Switch support (deko3d (devkitPro)) 8
18 PS2 (ps2gl), PS3 (RSXGL), PS4 (OpenOrbis/GNMX/GNM) support 8

WIP in detail

  • Font Manager
    • Update Windows font support to the new system
  • Update all Input to use PlatformBridge's implementation
  • Test ITextEditable.cpp#L119 on Linux
  • Text
    • Optimizations
  • TextBox
    • suppress text overflow
  • Button
    • suppress text overflow
  • Table
    • More cell types (TableImage, TableProgressBar); TableText, TableColor and TableCheckBox exist so far
    • Connect_/Disconnect_OnColorChange and OnCheckBoxChange: both signals are emitted when a cell's value changes but have no public connector yet
    • Checkbox inner padding (the gap between the box' border and its checkmark) is stored but not applied yet, so the checkmark fills the whole box
    • Auto track sizing (measure a track against its content); currently resolves like Fixed
    • Row/column spanning (the cells carry the spans already, the layout doesn't apply them yet)
    • Draw the header band's outer border (the width and colors are configurable but unused)
    • Keyboard traversal between cells (Tab); the arrow keys move the caret within a cell
    • Clipping is an axis-aligned scissor, so it stops being exact once the table carries a rotation
  • Slider / ProgressBar
    • The graph doesn't account for the track's border width, so an enabled graph paints over the frame; the indicator does account for it
    • A Circle indicator takes its corner radius from the indicator's x size alone, so it draws as a rounded rectangle whenever the two axes differ
  • IDropDown (shared base of ComboBox and ExtendedComboBox)
    • _borderColorsEnabled/Disabled/Hover/Clicked and _backgroundColorsEnabled/Disabled/Hover/Clicked are never read or written. The colors that actually reach the closed box come from IBorder's _borderColorEnabled and IBackground's _backgroundColorEnabled - one letter apart - so assigning the IDropDown ones looks right and changes nothing
    • The per-row border (_dropDownItemBorder) is declared but never built or drawn
    • Most of the protected surface is only touched inside IDropDown.cpp and could be private: setDropDownArrowColors, setDropDownBackgroundColors, setDropDownBorderColors, dropDownCenter, rotateLocalOffset, the three per-state drop-down color sets, _highLightColor, _dropDownArrowPathFill, the three ColorState fields, _zOffset, _arrowPadding and _maxItemsToDraw - subclasses reach all of them through the public setters and setColors
    • _dropDownBackgroundColorSate and _dropDownBorderColorSate are missing a t
  • ComboBox
    • Scrolling past the visible row cap; the list shows the first rows only, up to the cap
    • SetIndex neither clamps to the item count nor emits OnIndexChanged - only picking a row does
    • No SetEnabled override, so the disabled colors it carries never reach the elements
    • RemoveItem doesn't range-check its index and leaves the selected index pointing at whatever moved into that slot
    • Only the drop-down's corner radii are exposed (SetDropDownCornerRadii); the closed box has no public setter, and neither the panel's nor the arrow's fill type can be chosen, so their gradient fills are out of reach
    • The visible row cap is fixed at 8 with no setter
    • In the closed box the selected item's text can run under the arrow icon; only the border width is kept clear of it
  • Prefab
    • Children aren't registered with the DynamicLibWidgetManager, so a binding can't address them by string ID yet. That needs a deregistration path as well, or destroying a prefab would leave the manager holding freed pointers
    • The Lasagna is fixed at construction; a prefab keeps whatever AxisDefinition it was built with
  • Scene
    • Shared, cross-window scenes. SceneLoader::CreateScene hands back a std::unique_ptr, so a scene is one instance belonging to one window, and "the same scene in two windows" means instantiating the same description twice. Returning a shared scene is planned and needs three things: windows created with a shared GL context (glfwCreateWindow's share argument is nullptr today, so two windows currently share nothing at all), per-context VAOs over shared buffers (the spec never shares VAOs, FBOs or transform feedback, and Rectangle, Text, Mesh and Texture each own one), and splitting shared state from per-window view. Consequence until then: a 3D Model changed in one window does not propagate to another - they are separate objects with separate transform matrices. For 2D widgets sharing is incoherent anyway, because Window::UpdateLayout writes screen-space positions into the widgets and two differently sized windows need different values in the same fields; for 3D content it is coherent, and the shape it wants is one world with a camera per window, which belongs to the planned Environment container rather than to Scene
    • Widgets are owned by the generated _p struct, not by the Scene, and the Scene owns only its root Lasagna. Closing a scene therefore has to tear down its owner's entry too - SceneLoader registers scenes and borrows a pointer, but whoever holds the unique_ptr owns the lifetime, so CloseScene needs a release hook to make closing actually complete rather than just unregistering
    • SceneLoader owns no assets yet, so nothing holds what scenes share. When it does it has to be the single asset cache rather than a second one beside ResourceManager, and widgets need a way to reach it - they currently load their own textures, SVGs and models straight from a path in their constructors
    • Loading is synchronous. SceneLoadingProgress always reports 1.0 for a scene that exists, and becomes meaningful only once asset loading is split into an off-thread prepare step and a main-thread realize step - every gl* call has to stay on the context thread, and destruction can never leave it at all
    • No lifecycle signals (OnActivate, OnDeactivate, OnLoad, OnUnload), so application code has nothing to hook a scene transition to
    • Overlay scenes don't block the scene underneath them. Widgets hit-test inside their own Draw, so there is no central pass that could stop at the topmost hit the way a raycast would. Docking avoids it geometrically - a content scene is never given the strip, so it has nothing there to click - but a modal overlay needs the scene beneath it disabled, passing emitSignal = false to SetEnabled so the sweep doesn't fire every connected slot
    • Only a Lasagna can be the root container. That generalizes to an IContainer interface once ScrollView and Environment exist
    • SetLasagnaAxis takes its AxisDefinition by value, copying three vectors per call
  • SeparatorLine
    • The caption's left padding isn't clamped to the line's width, so a long caption or a large padding runs the gap and its text off the right end
    • The gap always starts from the left edge, so centering or right-aligning a caption means working out the padding by hand. SetTextAlignment doesn't do it either: the caption is always centered inside its own gap, and the alignment passed in is overwritten the next time the layout runs
  • ColorPreview
    • The checkerboard's two colors and its square size are fixed internals, so neither the board's contrast nor its scale can be set from outside
    • The dual preview is on by default, so a preview that was never asked to be split is
    • Against the light checkerboard the white half reads almost the same as the checkered one - compositing over white and over a white/light-grey board differ only inside the grey squares. Drawing the color at full opacity there instead would carry information the checkered half can't
    • Each rectangle rotates around its own center, so a rotated dual preview pulls its halves apart instead of turning as one piece
    • DualPrevieAlignment is missing a w
  • WindowBar
    • Window Icon
  • Window
    • line 375 check if updateProjection() can be made conditional again and also called at programmatic size change instead
  • Raster wrappers (IBackground, IBorder, IRangedValue, MenuBar)
    • The raster wrappers expose the Dotted settings only - no pattern selection and no secondary color - so a widget's background or border can't draw the Checkered pattern. Only a Rectangle owned directly can, which is why ColorPreview builds its own
  • IBackground
    • Make SetBackgroundImage() differenciate between image formats and treat SVGs as SvgImage and implement path color setters
    • Padding that automatically applies to the background image as well
  • AudioPlayback / MediaSource / Decoder
    • The FFmpeg half decodes, but nothing plays what it decodes yet. AudioPlayback still opens files through miniaudio alone, so only WAV, FLAC and MP3 are audible. Opus, Vorbis and audio inside containers (MKV/WebM/MP4) need a bridge: a custom ma_data_source that pulls frames from a Decoder and converts them with swr_convert
    • Decoder::Open refuses anything outside the codec allowlist in DecodingPolicy.hpp, so a build can't quietly start distributing a decoder it isn't licensed for. FreeCodecs holds the royalty-free and patent-expired ones (VP8/VP9/AV1, MPEG-2, MJPEG, FFV1, Theora, FLAC, Vorbis, Opus, MP3, 16-bit PCM); NonCommercialUseCodecs names the encumbered ones (H.264, HEVC, VVC, VC1, AAC) so a refusal can say which codec it refused and why. The FFmpeg build itself is LGPL 2.1 with GPL and nonfree off, and has to stay dynamically linked
    • FreeCodecs is narrower than the licensing situation: 24-bit and float PCM (WAV, AIFF), ALAC, ADX and Nintendo DSP ADPCM are all refused, although PCM has no patents at all and ALAC has been royalty-free since 2011. Each addition is a deliberate licensing decision rather than a code change
    • Packet owns an AVPacket's lifetime, but MediaSource::ReadPacket still takes a raw AVPacket* and leaves the unref to the caller. A Frame wrapper and a reader that runs the send / receive / drain / flush-after-seek protocol behind one call aren't built yet, so every caller currently repeats it by hand
    • A reader built on FindBestStream drops the packets of every other stream. That's right for audio alone; audio and video from one file will need a packet queue per stream
    • AudioMetadata's loop points (_Loop) aren't wired into AudioPlayback yet - ma_data_source_set_loop_point_in_pcm_frames is the hook
    • AudioMetadata doesn't read the Opus vendor string (it sits in OpusTags, which FFmpeg skips) or APE tags; WAV loops come from the first smpl loop only, and RF64/BW64/RIFX files aren't scanned for smpl at all
    • Cover art is handed over still encoded (JPEG/PNG bytes); a CoverArtCodec enum and decoded covers are planned
    • AHX and ADX encoding types other than 3 read but don't decode - FFmpeg has no AHX support at all, and its ADX decoder takes standard ADX only. Encrypted ADX decodes to noise without FFmpeg noticing, so AdxMetadata::_Decodable has to gate playback
    • One sound at a time. AudioPlayback owns a single ma_sound so it can be sought and queried, which means opening a second file replaces the first. Layered UI sounds want a second, fire-and-forget path alongside it
    • GetDuration is unreliable by format: Vorbis always reports 0 (an stb_vorbis push-mode limitation) and MP3 has to decode the whole file to answer, so it must be cached rather than polled
    • Playback speed shifts pitch with it, because it is resampling rather than time-stretching. miniaudio has no time-stretcher at all; pitch-preserving speed would mean routing through FFmpeg's atempo filter, which is already compiled into the build
    • Negative speeds do nothing - miniaudio drops any pitch <= 0, silently, through a void function. Reverse playback isn't a rate at all: it needs the decoded audio reversed by frame (not by sample, or the channels swap) behind a custom data source, which also gives up streaming
    • Levels are linear RMS with no dB helper, so a meter bound straight to them sits low and barely moves - music averages well under 0.3. A 20*log10 mapping belongs either in the meter widget or beside GetChannelVolume
    • The meter's decay is a fixed constant per processed block, so its feel shifts with the device's buffer size instead of with wall-clock time
    • No signals yet (OnPlaybackFinished, OnPositionChanged), so application code has nothing to hook a track ending to

Implemented Features

CLICK TO EXPAND
  • Widgets
    • Label
      • Render text
      • Text Alignment (Left, Right, Center)
      • Text Padding
      • Per-state text color (Enabled, Disabled, Clicked, Hover)
      • SetEnabled, SetPosition, SetSize, SetRotation
    • SeparatorLine
      • A rule for dividing content, with an optional caption set into it
      • Caption: font family, size, slant and weight, text padding, and distance from the line's left edge (SetTextLeftPadding), shown or hidden with ShowText
      • While the caption is shown the line carries a background gap sized to hold it, so the rule breaks around the text rather than running underneath it. Hiding the caption or clearing its text closes the gap again
      • The caption is clipped to whichever is taller, the line or the text itself, so a rule only a few pixels thick still draws its glyphs in full
      • Background (same options as Button)
      • SetPosition, SetSize, SetRotation
    • Button
      • Signals: OnClick, OnRelease, OnMouseEnter, OnMouseLeave, WhileHover
      • SetEnabled
      • Background: Solid Fill, Linear Gradient, Radial Gradient, HueStar Gradient
      • Border: Solid Fill, Linear Gradient, Radial Gradient, HueStar Gradient, Corner Radii, Border Width
      • Label text (shares Label's text properties)
      • SetPosition, SetSize, SetRotation
    • CheckBox
      • Represents a boolean value
      • Signals: OnClick, OnRelease, OnMouseEnter, OnMouseLeave, WhileHover
      • SetEnabled, SetValue, GetValue
      • Checkmark (SVG) with per-state colors (Enabled, Disabled, Hover, Clicked), Solid/Linear/Radial/HueStar Gradient fill
      • Inner fill field with per-state colors, Solid/Linear/Radial/HueStar Gradient fill, gradient offset/degree/animation & rotation speed
      • Optional binding to an external bool (UseInherietedValue + SetInheritValueReference) so the CheckBox mirrors a value owned elsewhere
      • Inner padding (distance between border and checkmark)
      • Background & Border (same options as Button)
      • Corner radii
      • SetPosition, SetSize, SetRotation
    • RadioButton
      • A single option belonging to a RadioButtonGroup
      • Signals: OnClick, OnRelease, OnMouseEnter, OnMouseLeave, WhileHover
      • SetEnabled, SetValue, GetValue
      • Indicator field with per-state colors (Enabled, Disabled, Hover, Clicked), Solid/Linear/Radial/HueStar Gradient fill, gradient offset/degree/animation & rotation speed
      • Indicator padding (distance between border and indicator)
      • Background & Border (same options as Button)
      • Corner radii
      • SetPosition, SetSize, SetRotation
    • RadioButtonGroup
      • Owns a set of RadioButtons and enforces mutual exclusivity, deselecting all others when one is selected
      • Register/Unregister RadioButtons dynamically, each paired with a Label
      • Grid layout: row/column axis definitions, with each cell holding a RadioButton + Label pair
      • Per-cell content alignment (Left, Right, Center) and content padding
      • Group text label (shares Label's text properties via ITextProperties)
      • Border (same options as Button), with configurable border gaps
      • SetPosition, SetSize, SetRotation
    • ComboBox
      • Picks one entry out of a list of text items: AddItem, RemoveItem, GetItemCount, SetIndex, GetSelectedIndex, GetSelectedText
      • Drop-down panel hanging off the bottom edge of the closed box, as tall as the item count up to a cap of visible rows, with its own per-state background and border colors and corner radii
      • Clicking the box opens and closes the list, clicking a row selects it and closes it, a click anywhere outside puts it away
      • The row under the cursor is highlighted, and the hit test walks the same row rectangles the renderer places, so what lights up is what gets picked
      • Arrow icon (shared SVG) with per-state colors, fitted to the icon's own aspect ratio inside the right edge of the box
      • Item text: font family, size, slant and weight, alignment and padding, inset by the border width so the frame never crosses a glyph
      • Signals: OnItemClicked, OnIndexChanged, OnClick, OnRelease, OnMouseEnter, OnMouseLeave, WhileHover
      • Background & Border (same options as Button)
      • SetPosition, SetSize, SetRotation: every part sits around the widget's center and its offset is rotated with the widget, so the open list stays attached to a rotated box
    • TextBox
      • Text input & editing (insert, backspace, key repeat)
      • Read-only mode
      • Caret: blinking with configurable blink time, colors, Solid/Linear/Radial/HueStar Gradient fill, gradient animation speed
      • Text selection: mouse-drag selection, colors, Solid/Linear/Radial/HueStar Gradient fill, gradient offset/degree/rotation speed, corner radii
      • Placerholder Text
      • Copy / Cut / Paste (via PlatformBridge's Clipboard)
      • Signals: OnTextChange, OnEnterPressed, OnEnterReleased, OnCopy, OnPaste
      • Background & Border (same options as Button)
      • SetPosition, SetSize, SetRotation
    • Slider
      • Represents a numeric value of any type (Bool, Int8-64, UInt8-64, Float32/64) with configurable min/max
      • Horizontal and Vertical orientation: the value always runs along the track's local x-axis, so a vertical slider is a horizontal one turned a quarter turn, while its own rotation stays at whatever the caller set. SetSize takes the on-screen footprint either way, so a layout container can size a vertical slider without knowing about the turn
      • Track direction (Normal/Inverted) chooses which end of the track holds the minimum, for values that count the opposite way to the track - a scrollbar's offset, for instance. Affects where the indicator and graph are drawn, not the value
      • Drag the indicator or click anywhere on the track to set the value
      • Step the value by a configurable step size, in whatever type the value currently holds
      • Indicator: Stroke or Circle type, sized per axis in pixels or percent of the track, per-state colors (Enabled, Disabled, Clicked, Hover), Solid/Linear/Radial/HueStar Gradient fill, Raster, Fog Effect, corner radii, border width and border gaps. Sized and positioned inside the track's border rather than over it
      • Graph (the filled part of the track): Bar or Wave mode, per-state colors, width, all fill types, corner radii
      • Signals: OnValueChanged, OnValueSet
      • Background & Border (same options as Button)
      • SetPosition, SetSize, SetRotation, corner radii
    • ProgressBar
      • Shares the Slider's value, indicator, graph and stepping machinery (IRangedValue) without the dragging
      • Horizontal and Vertical orientation, Normal or Inverted track direction
      • Signals: OnValueChanged, OnValueSet
      • Background & Border (same options as Button)
      • SetPosition, SetSize, SetRotation, corner radii
    • ColorPreview
      • Shows a color against a checkerboard, so an alpha below 1 reads as transparency instead of as a different color
      • The board is drawn by the Checkered raster pattern rather than by a texture, so its square size and its two colors are shader uniforms
      • Optional dual preview (EnableDualPreviewBackground): the widget splits in half and the same color is drawn over the checkerboard on one half and over white on the other, side by side or stacked (SetDualPreviewAlignment)
      • The previewed color and the border always span the whole widget, so the two halves differ only in what lies behind the color
      • Border (same options as Button)
      • SetPreviewColor, GetColor, SetPosition, SetSize, SetRotation
    • Table
      • Grid of typed cells, each created on demand the first time a value is assigned to it. The cell type follows from what is assigned: a string or a number makes a TableText, a color makes a TableColor, a bool makes a TableCheckBox
      • TableText cells: editable text, one Text mesh per cell (see the text interaction entry below)
      • TableColor cells: a solid color swatch. The cell stores only its color value and draws through a single rectangle shared by the whole table, so a color column costs one set of GL buffers no matter how many rows it has
      • TableCheckBox cells: a checkbox sized as the largest centered square that fits the cell minus a configurable margin, so it keeps its shape as the tracks resize. The cell stores only its checked state and draws through one background rectangle, one border rectangle and one SVG checkmark shared by the whole table, so a checkbox column costs one set of GL buffers regardless of row count
      • Checkbox interaction: the click target is the box itself rather than the surrounding cell, with hover and pressed states tracked per cell. Per-state colors (Enabled, Disabled, Hover, Clicked) for background, border and checkmark, plus corner radii and border width
      • Per-track sizing policies: Star (divides whatever viewport space the fixed tracks left over) and Fixed (absolute pixels, the mode that lets content outgrow the table and become scrollable)
      • Scrolling on both axes, with content extent, maximum scroll and scroll position exposed so an external Slider can drive it through the signal/slot mechanism
      • Only the tracks overlapping the viewport are drawn; partially visible ones are clipped by a scissor that intersects with whatever clip is already active, so a half-scrolled row can't spill past the border
      • Alternating track coloring with any number of color variants, banding by row or by column (TableOrientation)
      • Per-variant background, inner border and text colors
      • Column and row headers, placeable Top/Bottom and Left/Right, with their own font, alignment, padding, colors and border widths. The band is taken out of the content viewport and stays pinned while the content scrolls underneath it
      • Text interaction per cell: click to place the caret, drag to mark, arrow keys with key repeat, select all, copy / cut / paste, backspace. One caret and one selection highlight are shared by every cell, reused by whichever one currently has focus
      • Read-only tracks: marking and copying still work, only the edits are rejected. A checkbox in a read-only track still shows its value but won't toggle
      • Signals: OnTextChange, OnEnterPressed, OnEnterReleased, OnCopy, OnPaste
      • Background & Border (same options as Button)
      • SetPosition, SetSize, SetRotation
    • Image
      • Supports most image formats
      • Fit widget geometry to the texture's native aspect ratio
      • SetPosition, SetSize, SetRotation
    • SvgImage
      • Loads and rasterizes SVG vector graphics (lunasvg)
      • Fit widget geometry to the SVG's intrinsic aspect ratio
      • Per-path runtime recoloring of named SVG paths (Solid, Linear, Radial, HueStar Gradient) without re-rasterizing
      • SetPosition, SetSize, SetRotation
    • Model
      • Load 3D models and their material textures (Assimp)
      • Multiple meshes per model
      • SetPosition, SetSize, SetRotation
    • Lasagna (three-dimensional successor of Grid)
      • Align widgets in a three-dimensional pattern
      • Widget sizing policies (fixed size, expand X, Y, Z, XY)
      • Row, Column and Layer definitions, checkable before a Lasagna is built from them (non-empty, positive track sizes, within the per-axis track limit) - which matters for definitions arriving from another language
      • Row, Column and Layer spanning (a widget can occupy multiple cells along any axis)
      • AttachWidget reports whether the placement succeeded, so an out-of-range or already-occupied cell fails visibly instead of silently
      • Cell lookup by TrackIndex, and the current track count per axis
      • Debug lines
    • Prefab
      • A group of widgets laid out in its own Lasagna and treated as a single widget by whatever contains it
      • Composed, not subclassed: children are added with AttachWidget<T>(name, placement), so a prefab is a tree of widgets rather than a new C++ type per kind. A subclass per kind would have to expose its children through the C ABI, which addresses widgets by string and cannot hand back a pointer, so every prefab would grow the binding surface
      • Children are reached by name afterwards: GetChildWidget<T>, ShowChildWidget, IsChildWidgetShown
      • Owns its children; refuses a name that is already taken, and a cell that is out of range or occupied, without keeping the widget it was asked to build
      • SetPosition, SetSize, SetRotation, forwarded to its Lasagna
    • WindowBar
      • Top, Bottom Position
      • Close, Minimize, Maximize buttons (all shadered)
      • Toggleable elements (buttons, title, background, no-docking-drag zone)
      • Window Title
      • Shadered background
      • Per-button styling (background/border colors, gradients, corner radii, border width)
      • Maximize callback
      • Reserves space across the edge it sits on so content is laid out beside it rather than underneath it, or overlays the content instead (Window::SetWindowBarOverlaps) for designs where content is meant to run under the bar
    • Window
      • Background color or image
        • Background image ID can be used to create glass effects on widgets
      • Resizeable
      • Movable
      • Toggleable WindowBar
      • Holds an ordered list of Scenes it draws over its root Lasagna and under its WindowBar (AddScene, RemoveScene), borrowed rather than owned
      • Divides its surface into a client area: the WindowBar and then each docked Scene take their strip, and whatever is left goes to the overlay Scenes and the root Lasagna. Recomputed on resize, on adding or removing a Scene, and whenever a Scene changes what it reserves or whether it is active
  • Scenes
    • Scene
      • A named group of widgets with its own root Lasagna, hanging under a Window: Window / Scene / Container / Widgets. Meant for scene loading and for HUDs
      • Deliberately not an IWidget, only an IHierarchyNode. A scene has no geometry, no hit-testing and no color state of its own, and staying off IWidget keeps it out of Lasagna::AttachWidget - so a Scene can never end up nested inside a cell, and the hierarchy stays at a fixed depth
      • Contributes its name to its widgets' paths, so GetPath composes MainWindow/MyScene/RootLasagna/TestButton for the interoperability API and .bechaml to address
      • Active or Inactive (SetActive), which is a different axis from a widget's Enabled or Disabled: Active decides whether the scene draws at all, Enabled decides whether a widget reacts and which ColorState it renders in. Since widgets hit-test inside their own Draw, an inactive scene stops responding as well as drawing. A disabled widget still draws - so an active scene full of disabled widgets is exactly the dimmed-behind-a-modal case, and a Scene needs no separate visibility flag
      • Docks to an edge of the client area (SetReservedEdge with a DockEdge), reserving a strip in pixels that everything laid out after it is fitted around, so a menu bar or status bar can't hide or swallow clicks meant for the content behind it. ClearReservedEdge turns it back into an overlay
      • Overlay is the default: the scene covers the whole client area and reserves nothing, for floating panels and modals
      • Docked scenes reserve in the order they were added, so the first one to claim an edge also owns the corner where two claims meet
      • An inactive scene reserves nothing, and is laid out again when it is reactivated, so activating one never shows a layout computed for a different window size
      • SetPosition, SetSize, SetRotation, forwarded to its root Lasagna
      • Per-frame signal (Connect_OnUpdate / Disconnect_OnUpdate), emitted from Draw before the widgets draw, so a value written by a slot lands in the same frame instead of trailing one behind. Meant for widgets that track a live value, such as an audio level meter. An inactive scene doesn't draw and therefore doesn't tick
  • Media Playback
    • AudioPlayback
      • Plays a file through miniaudio's high-level engine, which owns the output device, decoder, resampler and mixer. WAV, FLAC and MP3 decode inside miniaudio itself, with no FFmpeg involved
      • Open / Start / Stop, with Stop rewinding to the start and the file staying loaded
      • Seeking and position reporting in milliseconds (Seek, GetPosition, GetDuration). miniaudio's own seconds-based calls convert against the file's sample rate rather than the device's, so a 44.1 kHz track on a 48 kHz device still lands where it should
      • Master volume, and playback speed via resampling (SetPlaybackSpeed). Speed and pitch are coupled, the way a record player couples them
      • Streamed rather than fully decoded into RAM, so an album-length FLAC costs no more memory than a short one. Seeking still works while streaming
      • Per-channel level metering (GetChannelVolume, GetChannelCount): a passthrough node measures RMS on the audio thread and publishes it through atomics, so reading a level from the GUI thread never touches an audio buffer and never blocks. Fast attack, slow decay, so it stays readable when polled once a frame
      • The engine is deliberately neither copyable nor movable: miniaudio objects are transparent structs whose address has to stay put
    • MediaSource
      • The demuxer, one per file. FFmpeg recognises the format from the content rather than the extension, so a WAV renamed to .bin still opens
      • Stream discovery (FindBestStream, GetStream, GetStreamCount), ReadPacket, and read-only access to the format context for metadata
      • Seeking in milliseconds: lands on the keyframe at or before the target, and allows for containers whose timeline doesn't start at zero
    • Decoder
      • One per stream: packets in, frames out, the same calls for audio and video. Checks the codec against the licensing allowlist when it opens
      • SendPacket / ReceiveFrame pass FFmpeg's codes through, so "needs more input", "finished" and "error" stay distinguishable; Flush discards buffered frames after a seek
      • Keeps the stream's index and time base, so a caller can route packets and turn frame timestamps into seconds
      • Verified sample-exact: a FLAC decodes to exactly the 16,689,792 samples its STREAMINFO header states
    • Packet: owns an AVPacket for its whole lifetime, so an early return can't leak one
    • AudioMetadata (ReadAudioMetadata)
      • Everything a file says about itself, without playing it. Tells WAV, FLAC (native or in Ogg), MP3, Ogg Vorbis, Opus, M4A, ADX and AHX apart by container and codec, never by extension
      • Common fields: duration, sample rate, channels and layout, bit depth, bit rate, all tags (merged from file and stream - Ogg keeps its comments on the stream), chapters and cue markers, embedded cover art, and whether the codec is on the allowlist
      • Per-format details: WAV smpl loops; FLAC block/frame sizes and audio MD5; MP3 ID3 versions, VBR/CBR and LAME gapless delay/padding; Vorbis vendor and bit-rate hints; Opus pre-skip, input rate and output gain; M4A brand, AAC profile and iTunes gapless info; ADX version, encryption and loop points (samples and bytes)
      • The sample count is what Decoder actually produces: Opus pre-skip, LAME gapless trimming (including the MP3 decoder's own 529-sample delay) and iTunes priming are taken out. Checked against full decodes of FLAC, MP3, Opus, Ogg Vorbis, Ogg FLAC and WAV files
      • One normalized loop region (_Loop, end exclusive) from wherever the format keeps it: the ADX header, a WAV smpl chunk, or LOOPSTART / LOOPLENGTH / LOOPEND tags
      • Reads FFmpeg leaves out: ADX loop points from the header (and its exact length - the demuxer only estimates one), WAV smpl chunks, the MP3 Xing/LAME header and the Vorbis vendor string
      • Still reads streams FFmpeg can't decode, such as AHX and type 4 ADX, whose channel count FFmpeg wipes once its decoder refuses them
    • Note for non-ASCII paths on Windows: file paths reach miniaudio as narrow strings and are opened with fopen_s, which reads them in the process codepage. An application that wants to open paths like …/電気グルーヴ/… needs an app manifest setting activeCodePage to UTF-8 (Windows 10 1903+), and MSVC's /utf-8 on its own target too - CMake's add_compile_options only reaches targets created after it. Without /utf-8, std::print also writes raw bytes the console misreads
    • NTFS doesn't normalize Unicode in file names: a name stored decomposed (NFD - common for files that came from macOS) and the same name typed in code (NFC) look identical but are different files. Paths taken from a file picker always match
  • Shaders
    • Solid Fill
      • Glass Effect
      • Rounded corners
      • Raster (Dotted, Checkered)
      • Fog Effect
    • Linear Gradient
      • Animated (rotation, motion)
      • up to 256 colors (RGBA)
      • Glass Effect
      • Rounded corners
      • Raster (Dotted, Checkered)
      • Fog Effect
    • Radial Gradient
      • Animated (rotation, motion)
      • up to 256 colors (RGBA)
      • Glass Effect
      • Rounded corners
      • Raster (Dotted, Checkered)
      • Fog Effect
    • HueStar Gradient
      • Animated (rotation, motion)
      • Glass Effect
      • Rounded corners
      • Raster (Dotted, Checkered)
      • Fog Effect
    • Raster (combinable overlay, works on every fill and border shader above)
      • Two patterns share the feature, picked with SetRasterPattern: Dotted and Checkered
      • One width curve drives both, sampled along a configurable direction (SetRasterDegree): each value is a dot's full width across, or a checkerboard square's side length, so the pattern's elements grow and shrink smoothly from one side of an element to the other
      • That same direction is the axis the pattern animates along, and it turns the checkerboard as a whole
      • Seamless, endless scrolling animation along that direction
      • Dotted: configurable color, spacing between dot centers, and opacity falloff (hard edge to soft center-only glow)
      • Checkered: a second color for the other square of each pair. Square edges are antialiased, and once squares fall below a pixel the board settles into an even mix of both colors rather than aliasing into moire
      • Alpha-blends over whatever the fill/border/glass effect already rendered; clipped by rounded corners
    • Fog Effect (combinable overlay, works on Solid Fill and Linear/Radial/HueStar Gradient; not the Border variants)
      • Dynamic, irregular cloud-like density from a multi-octave fractal noise field
      • Configurable overall opacity, drift speed, per-octave density/weight curve, and clearing threshold (how much clear/fog-free area shows through)
      • Alpha-blends over whatever the fill/raster pattern already rendered; clipped by rounded corners
    • Background Gaps (combinable, works on Solid Fill and Linear/Radial/HueStar Gradient; the Border variants carry their own Border Gaps instead)
      • Skips sections of the fill in a repeating solid/gap pattern: no gap, a single one, a set number of them, or tiled across the whole element
      • Offset and length are absolute pixels measured from the edge the pattern starts at, so each segment keeps its size and its distance from that edge as the element resizes, rather than stretching with it
      • The pattern runs along X and can be rotated to any angle, so the same definition gives vertical stripes, horizontal bands or anything between
      • A background is one element rather than four edges, so a single definition covers it - unlike Border Gaps, which describe one edge at a time
    • Dedicated Border variants of the above (Solid, Linear, Radial, HueStar), including Raster
    • Line Fill
    • Font Atlas Fill (used by the Text Renderer)
    • Texture
    • Shader Manager (compiles and caches all shaders once)
  • Text Renderer
    • Full Unicode codepoint range support via configurable glyph atlasses (FreeType)
    • Multiple font families, weights and slants, resolved from installed system fonts
    • Fallback/extended fontsets for filling in missing glyph ranges
    • Dual UTF-8 / UTF-32 string encoding (DoubleEncodedString), always kept in sync
    • Text Alignment (Left, Right, Center)
    • Text Padding & horizontal scroll offset for overflowing text
    • Glyph boundary lookup (used for caret positioning & selection)
  • Signals
    • Thread-safe Connect/Disconnect
    • Synchronous (Emit) and asynchronous, threaded (EmitAsync) dispatch
  • Input Manager
    • Per-window mouse position & button state
    • Focused/hovered window tracking
  • PlatformBridge (cross-platform system layer)
    • Windows & Linux support
    • Clipboard (copy/paste of text and bitmap data)
    • System font enumeration (family, weight, slant, Unicode ranges)
    • Keyboard input, held-key tracking, per-window key capture
    • Mouse button state & window-relative cursor position
    • Monitor enumeration (Windows)
    • Executable path & working directory utilities
  • Cross Language Capabilities
    • dll/so/dylib compilation
    • Widget ID Manager (DynamicLibWidgetManager) addressing widgets by string ID
    • Signal connection via C callbacks (OnClick, OnRelease, OnMouseEnter, OnMouseLeave, WhileHover, OnTextChange, OnEnterPressed, OnEnterReleased)
    • C#
    • Python

ToDos before milestone 5

CLICK TO EXPAND
  • Update and FixedUpdate loops (Scene::Connect_OnUpdate covers the per-frame case so far)
  • Frequency display for AudioPlayback, once the Histogram widget exists
    • Histogram first: bars driven by a per-index value array, which the Rectangle raster feature already has the uniform plumbing for
    • FFT via FFmpeg's av_tx (AV_TX_FLOAT_RDFT) - already linked through avutil, so it needs no new dependency and raises no new licensing question. Not FFTW, which is GPL
    • The transform must not run on the audio thread. The meter node already publishes through atomics; a spectrum needs the samples themselves handed over, which is what miniaudio's lock-free ma_pcm_rb is for
    • Log-spaced bands, a Hann window and dB magnitudes, or the bars pile into the bass and read as dead
  • Audio formats, in this order
    • The FFmpeg-to-miniaudio bridge first, so Opus, Vorbis and container audio actually play
    • WebM/MKA audio (needed for VP9/AV1 video anyway) and AIFF; WavPack and CAF are cheap extras
    • HCA - CRI's successor to ADX, used across PlayStation, Xbox, PC and mobile, with loop points like ADX
    • Tracker modules (libopenmpt, switched off in the current FFmpeg build), MIDI (needs a synthesizer such as TinySoundFont - and a SoundFont whose sample sources are clear for commercial use) and chiptune formats each need a new dependency
  • Text
    • Emoji support
    • Vertical text flow
    • Text Shaders with all shader options
    • Text Border
  • Shaders
    • shadow
    • cut corners
    • rename gradient fill to linear shift
    • radial shift
    • conic radiant shift
    • noise options
    • optimize shader handling
      • different IDs but only once compiled code
      • only pass parameters on initialzation and on parameter value change
  • WindowBar
    • Positioning, left, right
    • Docking emblem
    • Icon
  • Window
    • Docking
  • Font Manager
  • Cursor Icon Manager
  • Animation Engine
  • Test Carbon compatibility
  • Add more widget interactions to the DynamicLibWidgetManager
    • SetRotation, SetPosition, SetSize
    • Set Color/Decoration parameters
  • C++ modules once ALL common compilers (GCC, CLang, MSVC) support them
  • Wiki/Documentation
  • More text color properties
  • Rectangle, Text, Texture, IWidget: 3rd dimension
  • Complete Python binding
  • Refactor Binding
  • Refactor textures so a texture on a 3D model carries no unused quad (ITexture holds the standalone quad that only Image and Video draw with - VAO/VBO/EBO, the scaling/translation/rotation matrices, Draw/SetSize/SetPosition/SetRotation. Without a Projection the buffers are never created, but every Model texture still carries the members and the API. Move the quad out to what draws it)

Nice-to-have's

  • Better (relative) Viewports
  • Custom text renderer with many IANA character sets

Why?

There are many C++ GUI frameworks available, but most come with one or more drawbacks: restrictive licensing, enforced use of proprietary containers, inflexible window layouts, limited design capabilities, or poor cross-platform support, often confined to C++ only. RetroFuturaGUI aims to break these barriers!

Known Bugs

  • Window.hpp/.cpp
    • glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE()) causes objects with transparency to show what's behind the window even if the background is completely opaque
  • SvgTexture.cpp
    • Draw() switches the depth test off for its whole draw so that its coplanar mask passes can blend over the base layer. That also lets any SVG drawn later in the frame paint over whatever is already there, whatever the z - an open ComboBox drop-down is covered by an SvgImage sitting in a later Lasagna cell, for instance. Keeping the test on and using GL_LEQUAL for the mask passes would keep the blending and respect depth
  • TextBox.cpp
    • A TextBox reports WidgetTypeID::Button, so DynamicLibWidgetManager::SetText and ConnectSlot take the Button branch, dynamic_cast to Button* yields null and is then dereferenced. The WidgetTypeID::TextBox branches are unreachable as a result

Is AI used in this project?

AI is often used for repititive tasks like adding code comments to functions, classes, and structs and updating this readme's ToDo and feature list. Shaders are mostly written by AI and AI is sometimes used for finding bugs and to assist with complicated calculations. The architecture of font loading and Scene management was partially planned with AI. AudioMetadata.hpp/cpp are entirely written by AI. YUV to RGB conversion was done by AI

Popular repositories Loading

  1. RetroFuturaGUI RetroFuturaGUI Public

    A cross-platform, signal-slot-based GUI Framework written in C++ that can also be compile to dynamic libraries for use with other languages

    C++

  2. TestProject TestProject Public

    C++

  3. PlatformBridge PlatformBridge Public

    A cross-platform API wrapper to be compiled to static libraries

    C++