All notable changes to MaxPane will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Crash when capturing Docker or MaxPane itself — Added ancestor guard in
DoCapture()that rejects any window in MaxPane's parent chain, preventing circular reparenting crash. Docker and MaxPane are also filtered from the Open Windows menu. (#2, #4) - MIDI Editor survives project tab switches — New
dynamicTitlemechanism stores a stable search prefix ("MIDI take:") instead of the exact window title.CheckAliveautomatically recaptures the MIDI Editor when REAPER opens a new one after project switch or restart. (#3) was_visiblenot cleared on close — Addedwas_visible=0inWM_DESTROYhandler as safety net for all window destruction paths. "Close Container" menu now callsToggle()instead ofShutdown()directly. (#5)- Visual glitches — Hide close button on narrow tabs (< 60px); splitter hover timer now uses full hit testing; hover state cleared when context menu closes; adaptive text color (black on bright tab colors like yellow/cyan). (#7)
- Toolbar rendering and docking in MaxPane — Toolbars can now be properly captured and displayed. Removed toolbar filter from
FindWindowEnumProc. (#6) - Workspace switching: stale windows floating after restart — Windows from previous workspaces (toolbars, Actions, FX Browser, ReaImGui scripts) no longer appear as floating windows after switching workspaces and restarting REAPER. State-specific cleanup: double-toggle for state=0 windows, ShowWindow fallback for stubborn HWNDs, skip-toggle for script actions (state=-1). (#6)
- Toolbar icons disappearing on background click — Subclassed captured toolbar windows to block REAPER's internal drag-to-undock behavior. Clicking and holding on toolbar background no longer causes icons to vanish. (#6)
- Auto-detect toggle action —
LookupToggleActionnow auto-detects the REAPER toggle action ID for any window title (toolbars + known windows), ensuring proper state management for windows captured via Open Windows menu.
- macOS dark mode support — Pane background color now adapts to macOS dark/light appearance. Reparented windows (Track Manager, Routing Matrix, etc.) no longer show a light grey background when REAPER's dark mode is enabled. (#1)
- Solo/maximize pane — Temporarily expand any pane to fill the entire container. Toggle via context menu (Solo Pane / Exit Solo). Full tree snapshot is saved and restored when exiting solo mode.
- Tab reorder within pane — Drag tabs left/right within the same pane to rearrange their order. Visual insertion indicator shows the drop position.
- Splitter double-click reset — Double-click any splitter bar to reset its ratio to 50/50.
- Keyboard shortcuts via REAPER actions — Five new actions registered in REAPER's Actions dialog, bindable to any key:
- MaxPane: Next Tab
- MaxPane: Previous Tab
- MaxPane: Next Pane
- MaxPane: Previous Pane
- MaxPane: Solo Toggle
- TabEntry owned storage — All string fields (
name,searchTitle,actionCmd) are now ownedchar[]arrays instead ofconst char*pointers. Eliminates dangling pointer risk after tab moves/copies and removes theFixTabPointers()workaround. - ShiftTabsLeft helper — Extracted repeated tab-shift-and-clear pattern into a single function, used by CloseTab, MoveTab, and CheckAlive.
- InvalidateRect(…, FALSE) everywhere — Semantically correct now that
WM_ERASEBKGNDis handled; avoids unnecessary erase flicker.
- Open Windows menu validation — Added
IsWindow()check before using HWND from the open windows list, preventing crashes from stale window handles.
- Windows and Linux builds are included but have not been tested. Please report any issues at GitHub Issues.
- Region Render Matrix added to known windows (action 41888).
- Cross-platform support — Windows x64 and Linux x86_64 builds now compile and are included in releases alongside macOS.
platform.h— Central platform abstraction header replacing 5 scattered#ifdef _WIN32blocks.CreateMaxPaneDialog()— Portable dialog creation helper (nativeCreateDialogIndirectParamon Windows,SWELL_CreateDialogon macOS/Linux).
- Windows compilation — bridged
GWLP_USERDATA/SetWindowLongPtr(Win64 names) and replaced SWELL-onlySWELL_CreateDialogwith portable helper. - Linux compilation — suppressed SWELL
min/maxmacro conflict with STL viaWDL_NO_DEFINE_MINMAX. - Linker error on Windows/Linux —
ForceViewLayoutAndDisplay()now has an inline no-op for non-macOS platforms (Cocoa code is macOS-only).
- Known windows reorganized into logical groups (Mixing & Routing, Browsing & Media, Regions, Editing, Monitoring, Instruments).
- Removed
SWELL_PROVIDED_BY_APPfrom CMake global definitions — now set per-platform inplatform.h. - Removed CMake
get_target_property/list(REMOVE_ITEM)hack for Windows.
- MIDI Editor removed from known windows — cannot be toggled without an active MIDI item in the session.
- Renamed project: ReDockIt → MaxPane — all source, docs, CMake, actions, ExtState keys, RPP chunk tags updated.
- Diagonal grid lines in empty panes — subtle 45° lines on the pane background, disappear when a window is captured.
- Pane background color — captured windows (WS_CHILD) have no own background and inherited the parent's dark color. Fixed by setting
COLOR_PANE_BG = RGB(172,172,172)as a neutral light gray. - DoRelease toggle for Actions window — closing the Actions tab via [x] left REAPER's toggle state on (checkmark stayed). Root cause: SWELL destroys the NSWindow when a window becomes WS_CHILD, so
g_Main_OnCommandcouldn't find the window and opened a new one. Fix:SetParent(nullptr)to restore the NSWindow before toggling. - Hint text color — "Click header to assign a window" was white on light gray after the background color fix. Changed to
RGB(80,80,80).
- Pane menu button (▼) — 16 px button at the right edge of every tab bar. Left-click opens the pane context menu (same as right-click). Hit-test returns
-2; hover highlights the button. CalcTabBarLayoutshared helper — single source of truth for tab geometry used byDrawTabBar,TabHitTest, andIsOnTabCloseButton.GetTabRecthelper — returns the screen rect for a given tab index; used for targeted invalidation on color change.ExpandRectstatic helper (bothcontainer.cppandcontainer_input.cpp) — in-place RECT union, skips empty src/dst to prevent dirty-rect corruption.
- Targeted
InvalidateRect— hover/drag operations now invalidate only the affected rect instead of the full window:- Splitter hover: union of old + new splitter rect
- Tab/menu-button hover: union of old + new item rect via
GetTabRect/ inline button rect - Drag highlight change: union of old + new highlight pane rect
- Drag cancel/end: source tab bar + old highlight pane rect
- Tab color change: only that tab's rect via
GetTabRect - Timer hover timeout: cached old hover rects only
- Tab area now reserves
PANE_MENU_BTN_WIDTH(16 px) on the right for the menu button; tabs shrink accordingly.
TabHitTestx-bounds regression — the menu-button check (x >= paneRect.right - 16) was firing for clicks outside the pane's x range (e.g. in the adjacent right-side pane), causing the pane context menu to appear instead of tab interactions. Fix: checkx < paneRect.left || x >= paneRect.rightbefore the menu-button test.ExpandRectnow guards against emptysrcrect ({0,0,0,0}) in both call-sites, preventing the dirty rect from expanding into origin unnecessarily.
- Scroll arrows (
</>) for tab overflow — tabs shrink when a pane is narrow (original behaviour).m_tabScrollOffset,TAB_SCROLL_ARROW_WIDTH,TAB_OVERFLOW_THRESHOLDremoved.
- Splitter hover highlights (white bar on mouseover)
- Tab hover highlights (lighten effect on mouseover)
- Per-project state persistence via RPP files (
project_config_extension_t) StateAccessorabstraction for polymorphic state I/O (global, project, RPP)
- Workspace switch rendering: blank/artifact windows (e.g. Routing Matrix) after switching back
- Frameless floating windows when closing tabs via [x]
- Windows reappearing floating after REAPER restart despite being closed
- Shutdown lifecycle: proper toggle state check, reparent-before-toggle sequence
- Quit interception via hookcommand for reliable state save on macOS/Windows
- RepositionAll repaint:
SWP_FRAMECHANGED+InvalidateRectafter pane resize
- Simplified to global docker model (removed per-project visibility switching)
- RPP state I/O now uses synchronous
project_config_extension_t(replaced deferred timer) LoadWorkspaceusesReleaseAll(false)for smoother workspace transitionsDoCaptureforces frame recalculation viaSWP_FRAMECHANGED
- Startup deadlock: REAPER could hang when loading a project with MaxPane docked. The capture queue now defers
Main_OnCommandcalls duringLoadState()to avoid calling REAPER APIs while the project is still loading.
- Replaced raw
new/deletewithstd::unique_ptrfor safer resource management - Replaced all
strncpycalls withsafe_strncpyhelper for consistent null-termination - Extracted magic numbers (colors, geometry, timing) into named constants in
config.h - Merged duplicate
FindWindowEnumProc/FindChildWindowEnumProcinto single function - Debug logging is now conditional on
CMAKE_BUILD_TYPE=Debug(no longer always enabled) - Added
-Wshadowand-Wconversioncompiler warnings
- Dead code: unused
BuildLists()andIsAnyCaptured()methods
- Native C++ REAPER extension (no script dependencies)
- Binary split tree layout engine with up to 16 panes
- Tabbed window management with drag-and-drop between panes
- 14 known REAPER windows with one-click capture
- Arbitrary window capture (including ReaImGui scripts)
- Dock frame detection for ReaImGui windows
- Async capture queue with retry logic
- Named workspaces (save/restore complete layouts)
- Favorites system with persistent action command strings
- Tab color palette (8 colors)
- 5 built-in layout presets
- Auto-open on REAPER startup (configurable)
- Full state persistence via REAPER ExtState
- Dockable container (integrates with REAPER's docker system)
- Context menus for panes and tabs
- Capture-by-click mode
- Conditional debug logging (Debug builds only)
- Cross-platform architecture via WDL/SWELL