raym3 is a Material Design 3 inspired immediate-mode GUI library built on raylib. It provides a subset of Material Design 3 components with automatic resource management, making it easy to create modern, beautiful user interfaces in C++ applications.
Author: Jordan Miller (@nanofuxion)
The clip is a fresh capture of the v2 demo: same-frame layout, an editable text field, notification badge, selected chip, navigation indicator, bottom-sheet motion, carousel, checkbox motion, and shape morphs. The GIF is a visual sampler; the v2 component catalog and text/font/rendering features are listed below.
- Material Design 3 Inspired - Implements Material Design 3 design principles and components
- v2 Composable API - Retained
raym3::v2View tree with style objects, same-frame layout, a Material component catalog, text input editing, CSS transitions, a mutation-batch protocol, and Flutter-style CSS density scaling - Immediate-Mode API - Simple, intuitive API similar to raygui
- Automatic Resource Management - Icons and fonts are automatically discovered or can be embedded
- Optional Yoga Layout - Flexbox layout support for advanced UI composition
- Roboto Font Support - Roboto v3 (from roboto-3-classic); can be loaded from disk or embedded for portable deployment
- Light and Dark Themes - Full theme support with Material Design 3 color system
- SVG Icon Support - Material Design icons with multiple variations (filled, outlined, round, sharp, two-tone)
- Inline SVG Rendering - Rasterize and cache arbitrary SVG documents, including RGBA fills and opacity
- Platform Font Fallback - Native system UI fonts by default, CSS-family resolution, and glyph coverage fallback
- Transformed Overflow Clipping - Rounded clipping and hit testing follow nested rotation and scale transforms
- Zero External Dependencies - Can be built as a standalone library (raylib is fetched automatically)
- Scissor Stack API - Stack-based clipping with
PushScissor/PopScissor, debug visualization
raym3 currently implements the following Material Design 3 components:
- Button - Text, Filled, Outlined, Tonal, Elevated variants with keyboard activation and optional tooltips
- IconButton - Buttons with Material Design icons, keyboard activation, and optional tooltips
- TextField - Single-line text input with word/line navigation, double-click word selection, triple-click select all, word/character drag extension, Escape to revert, and I-beam cursor
- SearchBar - M3 contained search input with leading/trailing icon slots
- Checkbox - Standard checkbox with label, keyboard activation (Space/Enter), pointer cursor, and optional tooltips
- Switch - Toggle switch with keyboard activation (Space/Enter), pointer cursor, and optional tooltips
- RadioButton - Radio button with label, keyboard activation (Space/Enter), pointer cursor, and optional tooltips
- Slider - Value slider with keyboard navigation (Arrow/Page/Home/End), mouse wheel, focus ring, pointer/resize cursors, and optional tooltips
- RangeSlider - Multi-thumb range slider with Tab key thumb cycling, keyboard navigation, focus ring, and optional tooltips
- Chip - Assist, filter, input, and suggestion chips with M3 selection and state layers
- FloatingActionButton - FAB and extended FAB with regular/medium/large M3 Expressive sizes and color roles
- AppBar - Small, center-aligned, medium, and large app bars with navigation/actions
- Badge - Small status dots and large labeled badges for icons/navigation
- Card - Elevated surface container with multiple variants
- Dialog - Modal dialog with customizable buttons
- Modal - Full-screen modal component with backdrop and text input support
- Menu - Dropdown menu with leading/trailing icons, dividers, gaps, icon-only mode, disabled items, pointer cursor, and per-item tooltips
- List - Material Design list component with keyboard navigation (Arrow/Page/Home/End), Shift multi-select, Ctrl+A select all, typeahead search, drag reorder, pointer cursor, and per-item tooltips
- NavigationBar / NavigationRail - M3 destination navigation with active indicators, labels, and badges
- SegmentedButton - Segmented button groups
- ProgressIndicator - Circular and linear progress indicators
- Divider - Horizontal and vertical dividers
- Text - Typography component with multiple weights and alignments
- Icon - Material Design icons with multiple variations
- Container - Flexbox-based layout container (requires Yoga)
- View3D - 3D viewport component
- Tooltip - Hover tooltips with smart timing, configurable placement, and developer-defined text
- Snackbar - Temporary notification messages
- TabBar - Browser-style tab bar with closeable tabs, drag reorder, icons, and tooltips
Note: This is a partial implementation of Material Design 3. The immediate-mode list above names the established raym3:: components. The raym3::v2 catalog is broader, but catalog presence does not guarantee a complete spec implementation: some entries use generic token-driven rendering. See the v2 guide and component audit for coverage and limitations.
The v2 API builds a persistent View tree and lays it out in the current frame. Alongside View, Text, TextInput, Button, and custom render nodes, its M3Component catalog names: AppBar, Badge, Banner, BottomAppBar, BottomSheet, DataTable, DockedToolbar, FloatingToolbar, ButtonGroup, Button, Card, Carousel, Checkbox, Chip, DatePicker, Dialog, Divider, ExtendedFab, Fab, FabMenu, IconButton, List, LoadingIndicator, Menu, MenuItem, NavigationBar, NavigationBarItem, NavigationDrawer, NavigationRail, ProgressIndicator, RadioButton, RangeSlider, Search, SegmentedButton, SideSheet, Slider, Snackbar, SplitButton, Switch, Tabs, TextField, TimePicker, Toolbar, Tooltip, and Popover.
Core v2 capabilities include:
- Same-frame layout, retained node identity, transforms, rounded overflow clipping, and clipped hit testing.
- CSS-like styles, multi-layer and linear/conic gradients, transitions, density and OS font scaling, text spans, selection geometry, and selectable text.
- Text input with caret/selection, word navigation, multi-click selection and drag extension, plus host hooks for clipboard, haptics, and native editor integration.
- Native system UI font selection, CSS family/weight/style resolution, missing-glyph fallback, SVG view rendering, and platform emoji font support.
Component details and implementation caveats are tracked in the v2 guide. Start with v2 best practices when wiring up an app: it covers the build options, retained trees, and the per-frame input loop.
#include "raym3/raym3.h"
#include <raylib.h>
int main() {
InitWindow(800, 600, "raym3 Example");
SetTargetFPS(60);
raym3::Initialize();
raym3::SetTheme(false); // Light mode
char textBuffer[256] = "";
bool checked = false;
float sliderValue = 50.0f;
std::vector<float> rangeValues = {20.0f, 80.0f};
int selectedMenuItem = 0;
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(raym3::Theme::GetColorScheme().surface);
raym3::BeginFrame();
if (raym3::Button("Click Me", {100, 100, 120, 40})) {
// Button clicked
}
raym3::IconButton("home", {100, 150, 48, 48},
raym3::ButtonVariant::Filled,
raym3::IconVariation::Filled);
// TextField with custom background and automatic cursor inversion
raym3::TextFieldOptions options;
options.backgroundColor = PURPLE;
options.textColor = WHITE;
raym3::TextField(textBuffer, sizeof(textBuffer), {100, 210, 200, 56}, "Label", options);
raym3::Checkbox("Check me", {100, 280, 200, 24}, &checked);
// Slider with discrete mode and tick marks
raym3::SliderOptions sliderOpts;
sliderOpts.stepValue = 10.0f;
sliderOpts.showTickMarks = true;
sliderOpts.showStopIndicators = true;
sliderValue = raym3::Slider({100, 320, 200, 40}, sliderValue, 0.0f, 100.0f, "Slider", sliderOpts);
// Range slider for selecting a range
raym3::RangeSliderOptions rangeOpts;
rangeOpts.showValueIndicators = true;
rangeValues = raym3::RangeSlider({100, 380, 200, 40}, rangeValues, 0.0f, 100.0f, "Range", rangeOpts);
// Menu with icons, dividers, and gaps
static raym3::MenuItem menuItems[] = {
{"Home", "home"},
{"Settings", "settings"},
{nullptr, nullptr, nullptr, nullptr, true}, // Divider
{"Profile", "person"},
{nullptr, nullptr, nullptr, nullptr, false, true}, // Gap
{"Logout", "logout"}
};
raym3::Menu({100, 440, 200, 240}, menuItems, 6, &selectedMenuItem);
raym3::EndFrame();
EndDrawing();
}
raym3::Shutdown();
CloseWindow();
return 0;
}The TextField component supports several advanced features:
- Custom Colors: Set
backgroundColorandtextColorinTextFieldOptions - Automatic Cursor Inversion: Cursor color automatically inverts based on background luminance
- Password Mode: Set
passwordMode = trueto mask input - Input Masking: Use regex patterns via
inputMaskfor validation - Undo/Redo: Built-in support with configurable history depth via
maxUndoHistory - Icons: Add
leadingIconandtrailingIconwith click callbacks - Read-Only: Set
readOnly = trueto prevent editing - Multi-click Selection: Completed stationary double/triple clicks select words or all text; dragging after a multi-click extends the selection by word or character
raym3::SvgRenderer::DrawDocument(source, bounds, opacity) renders an inline SVG document into a cached texture. Repeated source documents reuse the bounded cache. This supports native v2::View SVG rendering as well as direct raym3 drawing. SVG fill RGBA and composed fill opacity are preserved.
On native platforms the default body face follows the system UI font. CSS family stacks can resolve platform fonts by family, weight, and style, with system fallback faces selected for missing glyphs. Emscripten continues to use bundled Roboto. Embedded resources do not change the native default.
Nested overflow clips account for rotation, scale, rounded corners, and hit testing. The renderer also fixes clipping balance, texture and atlas releases during a frame, and frame statistics when multiple rendering contexts are used.
The Menu component supports:
- Leading Icons: Display icons on the left side of menu items
- Trailing Text/Icons: Show additional text or icons on the right
- Dividers: Add visual separators with
isDivider = true - Gaps: Create spacing between items with
isGap = true - Icon-Only Mode: Set
iconOnly = truefor compact horizontal menus - Disabled Items: Set
disabled = trueto prevent interaction
The Slider component supports M3 Expressive customization:
- Discrete Mode: Set
stepValue > 0to snap to specific increments - Tick Marks: Enable
showTickMarksto display step indicators in discrete mode - Stop Indicators: Enable
showStopIndicatorsto show dots at min/max positions - Inset Icons: Add icons inside the track (
startIcon,endIcon) - Value Indicator: Show a value bubble above the thumb on drag (
showValueIndicator) - Start/End Text: Add text labels
- Track Colors: Customize active/inactive track colors
- Handle Color: Customize the handle thumb color
- End Dot: Customize or hide the end-of-track dot
The RangeSlider component provides multi-thumb range selection:
- Multiple Thumbs: Support for any number of thumbs via
std::vector<float> - Range Fill: Active track fill between first and last thumb
- Thumb Constraints: Thumbs cannot cross each other
- Minimum Distance: Set
minDistanceto enforce spacing between thumbs - Discrete Mode: Supports
stepValueandshowTickMarkslike Slider - Value Indicators: Show value bubbles for all thumbs when dragging
- Stop Indicators: Display min/max position markers
add_subdirectory(raym3)
target_link_libraries(your_target raym3)v2 layout uses Yoga flexbox, so alignItems, justifyContent and the rest behave like CSS. It is fetched automatically, or found as a sibling yoga directory. To use your own copy:
add_subdirectory(yoga)
add_subdirectory(raym3)
target_link_libraries(your_target raym3)RAYM3_USE_YOGA=OFF swaps in a basic stacking layout that ignores flex alignment.
raym3 requests 4x MSAA for the raylib window, so shapes have smooth edges. The request happens before main(), so no code is needed. To turn it off:
set(RAYM3_ANTIALIASING OFF CACHE BOOL "" FORCE)RayM3 can configure raylib's native Cocoa window without replacing raylib's renderer. Request a transparent framebuffer before creating the window, then configure Cocoa after InitWindow:
SetConfigFlags(FLAG_WINDOW_TRANSPARENT);
InitWindow(800, 600, "My transparent app");
raym3::platform::MacOSWindowOptions windowOptions;
windowOptions.transparentBackground = true;
windowOptions.fullSizeContentView = true;
windowOptions.transparentTitlebar = true;
raym3::platform::ConfigureMacOSWindow(windowOptions);For desktop transparency, clear/draw with alpha zero in areas that should reveal the desktop. raylib's FLAG_WINDOW_TRANSPARENT must be set before InitWindow; setting Cocoa's window opacity afterward cannot add the missing framebuffer alpha channel. Existing opaque ClearBackground calls will continue to produce an opaque surface. Cocoa compositing is native desktop transparency; it is not Liquid Glass, which is a system material effect rather than a general raylib drawing API. Set hideTitlebar to hide the native titlebar while retaining standard window controls. fullSizeContentView allows custom raylib content to extend into the titlebar area. These options are macOS-specific and are no-ops on other platforms.
Windows and Linux support is a future platform task: Windows needs evaluation of layered/composition window support and its interaction with raylib's swapchain; Linux support depends on compositor and X11/Wayland backend capabilities. Neither is implemented yet.
On macOS, SetMacOSWindowMaterialRegions places rounded AppKit blur materials behind specified raylib-coordinate regions. SetMacOSWindowDragRegions marks blank custom-titlebar areas that should initiate native window dragging. Drag regions intercept mouse input, so keep custom controls out of them. The native close, minimize, and zoom buttons still receive clicks and hover when a drag region covers them. In v2, Style.opacity applies opacity to an individual view/container and its rendered contents; background colors can also carry alpha independently.
Set MacOSWindowOptions::showWindowBorder to draw a native rounded edge around a transparent window (off by default; windowBorderCornerWidth can make the corners heavier than the edges), and call SetMacOSWindowControlsPosition to place the close, minimize, and zoom buttons in raylib window coordinates while retaining their native behavior.
RayM3 supports Roboto v3.015 from roboto-3-classic. Native UI text defaults to the platform system UI face; Emscripten uses the bundled Roboto faces. This applies to projects using RayM3 as a submodule or standalone on macOS, Windows, Linux, Android, and Web/WASM.
- Native UI default: SF, Segoe UI, or the platform fontconfig-selected face. It remains the default whether resources are embedded or not.
- Web UI default: bundled Roboto regular/bold faces; no system font file lookup is required.
- Resource embedding (
RAYM3_EMBED_RESOURCES): controls icon/resource embedding and does not switch the native system UI font to Roboto.
set(RAYM3_EMBED_RESOURCES ON CACHE BOOL "" FORCE) # Bundle icon/resources
add_subdirectory(raym3)To enable the layer-based input system with automatic input blocking and capture:
set(RAYM3_USE_INPUT_LAYERS ON CACHE BOOL "" FORCE)
add_subdirectory(raym3)
target_link_libraries(your_target raym3)Input Layers Features:
- Automatic Input Blocking: Higher Z-order layers automatically block input to lower layers
- Input Capture: Drag operations must start within component bounds (prevents accidental drags)
- Explicit Z-Ordering: Use
PushLayer(zOrder)andPopLayer()to control rendering order - Component Blocking: Cards and panels automatically block input to elements beneath them
- Overlay Layer Threshold: Layers with
zOrder >= 100bypass layout scissor clipping (for menus, tooltips, dialogs)
Usage Example:
raym3::BeginFrame();
// Layer 0 - Background
raym3::Button("Background", {100, 100, 120, 40});
// Layer 1 - Overlay panel
raym3::PushLayer(1);
raym3::Card({200, 150, 400, 300}, raym3::CardVariant::Elevated);
raym3::Button("Overlay Button", {220, 170, 120, 40});
raym3::PopLayer();
raym3::EndFrame();Input Capture for Viewports:
Rectangle viewportBounds = {100, 100, 400, 300};
// Register as blocking region
raym3::InputLayerManager::RegisterBlockingRegion(viewportBounds, true);
// Only process camera controls if drag started in viewport
bool canProcessInput = raym3::InputLayerManager::BeginInputCapture(
viewportBounds,
true // requireStartInBounds = true
);
if (canProcessInput) {
// Process camera rotation/zoom
if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) {
// Rotate camera
}
}raym3 supports building for Emscripten. The CMake configuration automatically sets GRAPHICS_API_OPENGL_ES3 for WebAssembly targets.
To embed SVG icons and fonts directly into the static library (no external files needed):
set(RAYM3_EMBED_RESOURCES ON CACHE BOOL "" FORCE)
add_subdirectory(raym3)
target_link_libraries(your_target raym3)Icon Optimization: By default, when RAYM3_EMBED_RESOURCES=ON, raym3 embeds the lightweight manifest in resources/icons/core-icons.txt, or a custom newline-delimited manifest provided with RAYM3_ICON_MANIFEST. This avoids embedding the full 10,000+ SVG catalog. If you need all icons embedded (e.g., for dynamic icon loading), set:
set(RAYM3_EMBED_RESOURCES ON CACHE BOOL "" FORCE)
set(RAYM3_EMBED_ALL_ICONS ON CACHE BOOL "" FORCE)
add_subdirectory(raym3)raym3 automatically discovers resources in the following order:
- Embedded Resources (if
RAYM3_EMBED_RESOURCES=ON) - CMake-defined resource directory (set at build time)
- Relative paths (
./resources/icons,./raym3/resources/icons, etc.)
Icons are expected in the structure:
resources/
icons/
filled/
icon_name.svg
outlined/
icon_name.svg
round/
icon_name.svg
sharp/
icon_name.svg
two-tone/
icon_name.svg
Fonts are expected in:
resources/
fonts/
Roboto/
Roboto-Regular.ttf
Roboto-Bold.ttf
...
raym3 embeds and installs a lightweight icon subset by default. To generate an app-specific manifest from source usage:
cd raym3 && python3 tools/generate_icon_manifest.py --root ../your-app --output resources/icons/app-icons.txtThen configure with:
set(RAYM3_ICON_MANIFEST "${CMAKE_CURRENT_SOURCE_DIR}/raym3/resources/icons/app-icons.txt" CACHE FILEPATH "" FORCE)Use RAYM3_EMBED_ALL_ICONS=ON or RAYM3_INSTALL_ALL_ICONS=ON only when dynamic icon names require the full catalog.
- raylib (required) - Automatically fetched via CMake FetchContent, default tag
6.0 - yoga (optional) - Flexbox layout engine, enabled via
RAYM3_USE_YOGA, default tagv3.2.1
See docs/v2-upgrade.md for the new v2 API, same-frame layout, dependency, and icon manifest details.
raym3 is built using the following excellent open-source projects:
-
raylib - A simple and easy-to-use library to enjoy videogames programming
- License: zlib/libpng
- Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
-
NanoSVG - Simple stupid SVG parser
- License: zlib/libpng
- Copyright (c) 2013-14 Mikko Mononen memon@inside.org
- Used for parsing and rasterizing SVG icons
-
Yoga - A cross-platform layout engine
- License: MIT
- Copyright (c) Meta Platforms, Inc. and affiliates
- Optional dependency for flexbox layout support
-
Material Design Icons - Material Design icon set
- License: Apache License 2.0
- Copyright (c) Google LLC
- Over 10,000 SVG icons included in multiple variations (filled, outlined, round, sharp, two-tone)
-
Roboto Font Family (v3.015) - Material Design typeface
- License: Apache License 2.0
- Copyright (c) Google LLC
- Native builds default to the system UI face; Emscripten defaults to bundled Roboto
- Material Design 3 - Google's Material Design system
- Design guidelines and specifications
- Color system, typography, and component specifications
raym3 is licensed under the zlib/libpng license, the same as raylib.
Copyright (c) 2025 Jordan Miller (@nanofuxion)
See LICENSE file for full license text.
Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.
We're particularly interested in contributions that add more Material Design 3 components to expand the library's functionality. Some high-priority components that would be valuable additions include:
- NavigationDrawer
- DataTables
- BottomSheet
- SideSheet
- Carousel
- Date/Time Pickers
If you find raym3 useful and would like to support its development, you can:
raym3 is an independent, self-contained project. Icons come from the material-design-icons submodule. Native UI text uses the platform system font by default, while Emscripten uses bundled Roboto. Resource embedding does not change the native font default.
Current Status: Partial implementation of Material Design 3 components. The library is functional and ready to use, but many components from the full Material Design 3 specification are not yet implemented.
This brings in the work done for Rayact: text spans and selection, real CSS gradients, retained layout, external views, a system UI font and OS text-size scaling. It has breaking changes, listed first.
LinearGradientnow starts withGradientKind kind, thenangleDegrees,centerX,centerY,stops.LinearGradient{90.0f, {...}}no longer compiles. Use designated initializers or assign the fields.TextLayoutOptions,PreparedSegment,RenderStatsgained members in the middle (fontStyle,maxLines,overflow,spanIndex,paintedCount). Positional brace-initialisation breaks or silently shifts values. Use designated initializers.v2::Nodehas a newconst stableId, so it can be copied but no longer assigned or swapped. Construct a new node instead of assigning.Node::preparedTextKeyis removed. It is replaced bypreparedTextGenerationandpreparedTextRevision. ComparepreparedTextCache->sourceand->optionsplus the generation instead.TextFieldVariantgainedPlain. Aswitchover it withoutdefaultwarns, and errors under-Werror=switch.ActiveAnimation::elapsedMsis now adouble. Only code that binds afloat&to it is affected.TextCacheKey(...)andBeginInputFrame(...)gained trailing defaulted parameters. Calls compile as before. Function pointers orstd::functionbound to the old signatures do not.TextStylegained many fields in the middle. Designated initializers are fine, positional ones break.Style.hnow includes<algorithm>andDensity.h.- CMake: with
RAYM3_USE_YOGA=ON,RAYM3_FETCH_DEPS=OFFand noyogacoretarget, configure now stops with an error. It used to warn and turn Yoga off.
- Gradient angles follow CSS.
0points up and angles run clockwise, so180is top to bottom and90is left to right. Before, angles snapped to four blends and90painted top to bottom. Multi-stop gradients now honour every stop. Rewrite90as180for a vertical gradient. - Alpha is honoured for
backgroundColorandborderColor. Semi-transparent colours used to paint opaque. font-sizeis the em size. Text draws roughly 10-15% larger than before (a 24 px label measured about 11% wider) and wraps sooner, so layouts shift.- The default font on native platforms is the system UI font (SF, Segoe UI, fontconfig), not Roboto. Roboto is the default only on Emscripten.
- Long unbreakable words wrap at the line width. They used to produce one letter per line.
- OS text scale applies to text and text fields once the host calls
Density::SetFontScale. The default is 1.0, so nothing changes until then. Opt out per run withallowFontScaling = false. - Node ids are never reused.
DisposeNodenow removes the node from its parent, and clearsrootif the root is disposed. It used to stay alive and keep painting. - Scrolling: the fling velocity tracker scales to the real frame rate, and a second flick within 400 ms adds the leftover momentum of the first.
- Fixed-position z-order uses a precomputed key. The old sort had undefined behaviour, so ties may order differently.
- Clipping: a scissor that does not overlap now pushes an empty rect, which fixes an unbalanced clip stack. Vulkan (
rlvk) uses real stencil rounded clipping. WebGPU (rlwg) falls back to a square scissor. - Emoji: Windows uses DirectWrite and macOS/iOS use CoreText. A custom emoji font now overrides the OS rasteriser.
overflow: hiddenstill clips to the outer edge of the border, not the padding box. This is unchanged, but note it if you put images in bordered, rounded nodes.
- TextField double-click selects a word, triple-click selects all, and dragging after multi-click extends selection.
- Native UI uses platform system fonts with CSS-family resolution and missing-glyph fallback; WebAssembly continues to use bundled Roboto.
- Added cached inline SVG document rendering with RGBA fill and opacity support.
- Fixed transformed rounded overflow clipping and descendant hit testing, along with renderer resource lifetime and frame-stat handling.
- Improved emoji font selection and rendering across supported platforms.
- Yoga flexbox layout and 4x MSAA anti-aliasing are on by default (
RAYM3_USE_YOGA,RAYM3_ANTIALIASING). v1Layoutwas a no-op without Yoga. - v2 TextInput follows HTML input rules with a mouse: mousedown focuses, double-click selects a word, drag extends by words,
autoFocusworks. - Cursor no longer flickers or sticks as an I-beam;
EndFrameonly changes the OS cursor when the request changes. - v1 checkbox, radio, and switch draw vector marks instead of an icon font raym3 does not ship.
- New
example_v1_demo, rebuilt v2 demo, and docs: v2 best practices and v1 immediate mode.
- Selectable
Text(TextStyle::selectable) with shared selection geometry, and styled spans inside oneTextnode. - CSS gradients: linear and conic, evaluated per pixel with dithering, multiple background layers,
border-areaclipping. ExternalViewfor native views embedded in the tree.- Percent dimensions, per-edge borders, caret and selection colours,
maxLines,overflow, underline and line-through. Density::Set/GetFontScale,FontManagerglyph-cache helpers, system UI font lookup.RetainedLayout*(opt-in),CancelFling,ReplayCurrentClips,SetSelectionMenuHost,SetHostClickCount.
- Rounded gradients drew nothing on plain raylib OpenGL/GLES. The gradient meshes (linear, conic and border-area) wind their triangles clockwise on screen, and raylib's plain GL path culls those. Culling is now switched off around each mesh, with the batch flushed on both sides. Checked by rendering linear gradients on macOS OpenGL 3.3. Conic and border-area got the same change but were not rendered, and the Metal, Vulkan and WebGPU backends were not run.
- Rounded corners on a horizontal gradient came out chamfered. The extra rows for the corner were spread over the whole height. They are now packed into the two corner bands.
- Layout no longer marks every node dirty each frame. Text cache keys are no longer rebuilt on every measure.
- Clip-stack balance, mid-frame atlas and texture frees, frame stats read from the context that rendered.
- Scissor Stack API: Public
PushScissor,PopScissor,BeginScissor,GetCurrentScissorBoundsfor custom clipping regions. Scissors intersect and stack correctly. - Scissor Debug:
SetScissorDebug,IsScissorDebug,DrawScissorDebugfor visualizing active scissor regions. - Overlay Layer Threshold: Input layers with
zOrder >= 100bypass layout scissor (menus, tooltips, dialogs render above clipped content). - Emscripten: Automatic
GRAPHICS_API_OPENGL_ES3for WebAssembly builds. - Layout: Scroll containers and
GetActiveScissorBoundsnow use the shared scissor stack. - Slider, TextField, RangeSlider: Use
PushClipRect/PopClipRectto restore parent clip. For custom content in scroll containers, callPushScissor(Layout::GetActiveScissorBounds())andPopScissor().
- Layout Flicker Fix: Eliminated UI flicker when layout structure changes (adding/removing elements, switching tabs) by implementing stable hash-based node IDs instead of sequential numbering.
- Stable Node IDs: Layout node IDs now use FNV-1a hashing based on tree hierarchy rather than sequential order. IDs remain stable when siblings are added/removed.
- New Layout API: Added
PushId(const char*),PushId(int), andPopId()for advanced manual ID control in complex layouts. - Backward Compatible: All existing code continues to work.
SetIdOffsetmaintains compatibility while integrating with the hash-based system.
- Buffered Cursor System: Added
RequestCursor()API - cursor is set once per frame inEndFrame()to prevent flickering when multiple components render. Components callRequestCursor()instead ofSetMouseCursor()directly. - Tooltip Smart Timing: Tooltips show instantly (50ms) after the first tooltip in a session. Returns to normal delay after 2 seconds of inactivity.
- Tooltip Support: Added optional
tooltipandtooltipPlacementfields toButtonOptions,SliderOptions,RangeSliderOptions,IconButtonOptions,CheckboxOptions,SwitchOptions,RadioButtonOptions,ListItem,MenuItem, andTabItem. - TextField Enhancements: Reduced horizontal padding, fade gradient for overflow text, triple-click select all, Escape to revert, Cmd+Backspace/Alt+Backspace word/line deletion, auto-scroll during drag selection, I-beam cursor on hover.
- Slider & RangeSlider: Added focus management, keyboard navigation (Arrow keys with Shift 10x, Page Up/Down 20%, Home/End min/max), mouse wheel support, focus ring, pointer/resize cursors.
- RangeSlider: Tab key cycles between thumbs when focused.
- List Component: Added keyboard navigation (Up/Down/Home/End/Page Up/Page Down), Shift+Arrow for multi-select range, Ctrl+A select all, Escape to deselect, Enter to activate, Space to toggle, typeahead search with 500ms timeout.
- Menu Component: Added pointer cursor on hover for icon-only and standard menus.
- TabBar: Added tooltip support for individual tabs.
- Button & IconButton: Added keyboard activation (Space/Enter) when focused, pointer cursor on hover.
- Checkbox, RadioButton, Switch: Added keyboard activation (Space/Enter), pointer cursor, focus management with click-away blur.
- Focus Management: All focusable components lose focus when clicking outside their bounds.
- Tooltip Component: New
Tooltip()function andTooltipManagerfor deferred tooltip rendering - TabBar Component: Browser-style tab bar with closeable tabs, drag reorder, loading/audio indicators, add tab callback, and text truncation
- Removed Native Text Input: The
useNativeInputoption inTextFieldOptionshas been removed along with theRAYM3_ENABLE_NATIVE_TEXT_INPUTCMake option. The TextField component now provides full native-like text editing behavior (keyboard shortcuts, word/line navigation, selection, undo/redo) without requiring platform-specific backends. This simplifies cross-platform deployment and removes the Cocoa framework dependency on macOS.
raym3 provides a stack-based scissor (clipping) API for custom clipping regions:
PushScissor(Rectangle bounds)- Push a clipping region; intersects with current scissorPopScissor()- Pop the current scissor and restore the previous oneBeginScissor(Rectangle bounds)- Convenience forPushScissor(bounds)GetCurrentScissorBounds()- Get the current active scissor rectangleSetScissorDebug(bool enabled)/IsScissorDebug()- Enable scissor debug overlayDrawScissorDebug()- Draw green outlines of active scissor regions (call after your frame content)
The layout system and scroll containers use this API internally. Use layers with zOrder >= 100 for overlays (menus, tooltips, dialogs) so they render above layout clipping.
raym3 provides built-in tools for debugging layouts and understanding component bounds:
The layout system includes a powerful debug visualization tool that helps you understand how your UI is being laid out:
// Enable debug visualization
raym3::Layout::SetDebug(true);
// In your render loop, after Layout::End():
raym3::Layout::DrawDebug();Features:
- Color-Coded Boxes: Each layout node gets a unique color generated from its index, making it easy to distinguish between different components
- Hover Highlighting: When you hover over a component, it darkens and becomes more opaque with a darker outline for clear identification
- Low-Opacity Overlays: Non-hovered components use very low opacity (5%) so they don't obscure your actual UI
- Visual Hierarchy: Helps you understand container nesting, padding, gaps, and flexbox behavior
Usage Tips:
- Enable debug mode when your layout isn't behaving as expected
- Hover over components to see their exact bounds
- Use it to verify padding, gaps, and alignment are working correctly
- Toggle it on/off at runtime to compare the visual layout with the debug overlay
When building tabbed interfaces or MDI (Multiple Document Interface) applications, each tab needs its own isolated layout state. Use these methods to prevent layout bleeding between tabs:
// Set a unique ID offset for each tab (e.g., tabIndex * 1000)
raym3::Layout::SetIdOffset(activeTabIndex * 1000);
// When switching tabs, invalidate previous frame bounds to force fresh layout
raym3::Layout::InvalidatePreviousFrame();When to use:
- Call
SetIdOffset()beforeLayout::Begin()when rendering each tab's content - Call
InvalidatePreviousFrame()when switching between tabs to reset layout state
- Click on Release: Components (Buttons, etc.) trigger their primary action on mouse release while hovering, rather than on press. This matches standard UI behavior and allows users to cancel a click by moving the mouse away before releasing.
- Input Capture: Drag operations (sliders, scrollbars) capture input, allowing the user to drag outside the component bounds once the gesture has started.
- Buffered Cursor: Cursor changes are buffered via
RequestCursor()during the frame and applied once inEndFrame(). This prevents flickering when multiple components render. - Focus Management: Interactive components track focus state. Clicking a component gives it focus; clicking away removes focus. Focused components respond to keyboard input (Space/Enter for activation, Arrow keys for navigation).
- Tooltip Smart Timing: After the first tooltip is shown, subsequent tooltips appear instantly (50ms) for 2 seconds, then revert to the normal delay. This matches native OS tooltip behavior.
Note: This README was generated with the assistance of AI and may contain errors. Please verify all information and report any issues.
