# TULayoutView Toolbar Integration in Toon Boom This document explains how toolbars are created, managed, and displayed within TULayoutView instances in Toon Boom Harmony Premium and Storyboard Pro. **IDA Databases:** - `RE/HarmonyPremium.exe.i64` - Main application - `RE/ToonBoomActionManager.dll.i64` - AC_Toolbar and AC_Manager implementations - `RE/ToonBoomLayout.dll.i64` - TULayoutView and TULayoutFrame implementations ## Overview Toon Boom uses a two-tier toolbar system: 1. **Global Toolbars** - Application-wide toolbars (FileToolbar, EditToolbar, DrawingToolToolbar, etc.) 2. **View-Specific Toolbars** - Toolbars that appear when a specific view has focus (DrawingViewToolbar, TimelineViewToolbar, CameraViewToolbar, etc.) ## Toolbar Buttons Disabled? If your custom view’s toolbar shows but every button is disabled, the root cause is almost always **validation / responder resolution**, not the toolbar XML. See `docs/AC_Toolbar_ButtonEnablement.md:1` for the IDA-verified validation flow and why `enabled="true"` is ignored. ## Important: raiseArea vs Toolbar Display **`TULayoutManager::raiseArea()` does NOT directly display toolbars.** `raiseArea` is used to bring a view/area to the foreground: ```cpp // Signature: TULayoutView* raiseArea(const QString& areaName, TULayoutFrame* frame, bool createNew, const QPoint& pos); // Example usage (from HarmonyPremium.exe): layoutManager->raiseArea("Colour", nullptr, true, QPoint(0,0)); // Shows Colour palette layoutManager->raiseArea("Morphing", nullptr, true, QPoint(0,0)); // Shows Morphing view ``` **Toolbar display is triggered separately** through: 1. `TULayoutFrame::setCurrentTab(...)` (tab/view change) 2. `TULayoutFrame::showViewToolBar()` (updates the view-toolbar for the new current view) 3. `view->toolbar()` (returns toolbar definition as a `QDomElement`) `TULayoutManager::setCurrentLayoutFrame(frame)` does **not** call `showViewToolBar()` directly (verified in ToonBoomLayout.dll `0x7ffa0be541d0`). If you call `raiseArea` and don't see toolbars, the likely causes are: - Your view's `toolbar()` override returns an empty `QDomElement` - The toolbar XML hasn't been registered with `AC_Manager` - The view isn't properly receiving focus after being raised - `LAY_ToolbarInfo::m_isDefault` is still non-zero (the frame hides/resets the view-toolbar in this case) ## Architecture ``` ┌─────────────────────────────────────────────────────────────────┐ │ TULayoutManager │ │ - Manages all frames, areas, and global toolbars │ │ - showToolbar() at vtable+416 creates/shows toolbars by name │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ TULayoutFrame │ │ - Contains TULayoutMainWindow for toolbar docking │ │ - showViewToolBar() displays view-specific toolbar │ │ - Stores LAY_ToolbarInfo per view for configuration │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ TULayoutViewHolder │ │ - Contains 1-2 TULayoutView instances │ │ - Notifies frame when active view changes │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ TULayoutView │ │ - Abstract base class for all views │ │ - toolbar() virtual method returns QDomElement │ │ - setToolbarInfo() receives LAY_ToolbarInfo │ └─────────────────────────────────────────────────────────────────┘ ``` ## Toolbar Definition and Creation Flow ### 1. Toolbar XML Loading During application startup, toolbars are loaded from `toolbars.xml`: ```cpp // From HarmonyPremium.exe session initialization (0x140034EB0) // Loads toolbars.xml via AC_Manager::loadToolbars(path, outIds) (vtable+0x178) UT_String toolbarsPath = RM_GetResourcePath("toolbars.xml"); QList toolbarIds; actionManager->loadToolbars(toolbarsPath.ToQString(), toolbarIds); ``` The `toolbars.xml` file defines toolbar content using XML elements like: ```xml ``` ### 2. Global Toolbar Initialization Global toolbars are shown during session initialization (`sub_14002F840`): ```cpp // Show global toolbars via TULayoutManager::showToolbar (vtable+416) layoutManager->showToolbar("FileToolbar", true); layoutManager->showToolbar("EditToolbar", true); layoutManager->showToolbar("DrawingToolToolbar", true); layoutManager->showToolbar("ArtLayerToolbar", true); // Conditional toolbars based on features if (WHO_Features::hasOnionSkinToolbar()) { layoutManager->showToolbar("OnionSkinToolbar", false, true, nullptr); } ``` **Key Function Addresses (HarmonyPremium.exe):** - Session toolbar init: `0x14002F840` - `TULayoutManager::showToolbar` is called via an import thunk in HarmonyPremium.exe (address is version-specific) ### 3. View-Specific Toolbar Definition Each view class can override `TULayoutView::toolbar()` (ToonBoomLayout.dll vtable `+0x58`) to provide a `QDomElement` describing the view-toolbar content. Implementation detail (MSVC x64 ABI): this return-by-value is implemented with a hidden return buffer pointer (passed in `RDX` for member functions), which is why decompiled code often looks like `toolbar(this, out)`. In Harmony Premium, many views inherit from `TUWidgetLayoutView` (via `VL_ViewQt`), so the `TULayoutView*` you get in `toolbar()` is an embedded subobject: - `TUWidgetLayoutView::m_actionManager` is stored at `+0x30` (in the `TUWidgetLayoutView` object) - the embedded `TULayoutView` base is at `+0x68` - therefore, `AC_Manager*` is reachable from a `TULayoutView*` as `*(AC_Manager**)((char*)this - 0x38)` **Example: Drawing toolbar override** - Function: `VL_BaseDrawingViewQt__toolbar` - Address: `0x1403B9880` (HarmonyPremium.exe) Behavior (from disassembly/decompilation): - Read `AC_Manager*` from `[this-0x38]` - Call `AC_Manager` vfunc at vtable `+0x1A0` to fill a `QDomElement` for id `"DrawingViewToolbar"` - Return an empty `QDomElement` if `AC_Manager` is null **Example: Reference View toolbar override** Toon Boom’s “Reference View” uses the `ModelViewToolbar` definition (see `toolbars.xml`), implemented in: - Function: `VL_ModelViewQt__toolbar` - Address: `0x1403C8A20` (HarmonyPremium.exe) Behavior: same as above, but requests `"ModelViewToolbar"`. **Relevant TULayoutView virtuals (ToonBoomLayout.dll):** - `toolbar(...)`: vtable `+0x58` - `setToolbarInfo(const LAY_ToolbarInfo&)`: vtable `+0x60` ### 4. Toolbar Display Flow When the current tab/view changes, Toon Boom updates the frame’s view-toolbar via `TULayoutFrame::showViewToolBar()` (ToonBoomLayout.dll `0x7ffa0be4bb70`). Confirmed call sites include: - `TULayoutFrame::setCurrentTab(TULayoutViewHolder*)` → `showViewToolBar()` - `TULayoutManager::showViewToolBars()` → `TULayoutFrame::showViewToolBar()` High-level behavior of `showViewToolBar()` (validated from disassembly): 1. `currentView = m_layoutManager->findInstance(this, false)` 2. `toolbarEl = currentView->toolbar()` (calls the view’s override) 3. `info = currentView->getToolbarInfo()` (copy of `TULayoutView::m_toolbarInfo`) 4. Hide/reset cases: - If `info.m_isDefault != 0`: clear + hide the frame toolbar and return - If `toolbarEl` is null: clear + hide the frame toolbar and return 5. Ensure `m_viewToolbar` exists (created once via `createEmptyToolBar(info.m_name)` if needed) 6. Populate the toolbar: - `m_viewToolbar->setOwner(currentView->getWidget())` - `m_viewToolbar->changeContent(toolbarEl, info.m_buttonConfig, info.m_buttonDefaultConfig)` 7. Dock and apply placement: - `m_mainWindow->addToolBar(info.m_toolBarArea, m_viewToolbar->toQToolBar())` - `Layout::Toolbar::loadToolbar(info, m_mainWindow)` - `m_viewToolbar->setVisible(info.m_visible)` ### 4.1 raiseArea Function Details The `raiseArea` function is for **bringing a view area to the foreground**, not for toolbar display: ```cpp // Function signature (from ToonBoomLayout.dll) TULayoutView* TULayoutManager::raiseArea( const QString& areaName, // e.g., "Colour", "Morphing", "Timeline" TULayoutFrame* frame, // Target frame, or nullptr for auto-selection bool createNew, // true = create new instance if needed const QPoint& pos // Position for floating windows ); ``` **Example usages found in HarmonyPremium.exe:** ```cpp // sub_14086E410: Opens the Colour palette view void showColourView() { TULayoutManager* mgr = getLayoutManager(); QString areaName("Colour"); QPoint pos(0, 0); mgr->raiseArea(areaName, nullptr, true, pos); } // sub_1401579C0: Opens Morphing view on double-click // (called from QAbstractItemView::mouseDoubleClickEvent handler) void openMorphingOnDoubleClick() { TULayoutFrame* frame = view->getLayoutFrame(widget); TULayoutManager* mgr = frame->getLayoutManager(); QString areaName("Morphing"); QPoint pos(0, 0); mgr->raiseArea(areaName, nullptr, true, pos); } ``` **Key points:** - `raiseArea` returns the `TULayoutView*` that was raised/created - The third parameter `createNew` controls whether to create a new tab/instance - Pass `nullptr` for `frame` to let the system choose the target frame - Toolbar display is a **separate mechanism** triggered by focus changes ### 4.2 Ensuring Toolbar Display After raiseArea If you need to ensure toolbars appear after calling `raiseArea`: ```cpp // Method 1: Let the focus system handle it naturally TULayoutView* view = layoutManager->raiseArea("MyArea", nullptr, true, QPoint(0,0)); if (view) { QWidget* widget = view->getWidget(); if (widget) { widget->setFocus(Qt::OtherFocusReason); // Triggers focus chain } } // Method 2: Explicitly trigger the toolbar display TULayoutView* view = layoutManager->raiseArea("MyArea", nullptr, true, QPoint(0,0)); if (view) { TULayoutFrame* frame = view->getLayoutFrame(view->getWidget()); if (frame) { // Direct refresh paths observed in ToonBoomLayout.dll: // - TULayoutFrame::setCurrentTab(...) calls frame->showViewToolBar() // - TULayoutManager::showViewToolBars() calls into the current frame(s) frame->showViewToolBar(); layoutManager->showViewToolBars(); } } ``` ## AC_Toolbar Integration Toolbar definitions and instances involve three different components: - `AC_Manager` (ToonBoomActionManager.dll): loads `` definitions (e.g., from `toolbars.xml`) and can create `AC_Toolbar` instances. - `TULayoutManager` (ToonBoomLayout.dll / HarmonyPremium.exe): shows/hides *global* toolbars by name. - `TULayoutFrame` (ToonBoomLayout.dll): owns the *view* toolbar instance (`m_viewToolbar`) and updates it on tab/view changes via `showViewToolBar()`. ### How Toolbars Are Actually Registered 1. **Toolbar Definitions are loaded from XML** (`toolbars.xml`): ```cpp // During session init (sub_140034EB0 in HarmonyPremium.exe) AC_Manager* actionManager = AC_CreateActionManager(keywords); // Load toolbars via AC_Manager::loadToolbars(path, outIds) (vtable+0x178) QString path = RM_GetResourcePath("toolbars.xml").ToQString(); void** vtable = *reinterpret_cast(actionManager); using LoadToolbarsFromFileFn = void (__fastcall *)(AC_Manager* self, const QString* path, QList* outIds); auto loadToolbarsFromFile = reinterpret_cast(vtable[47]); // vtable+0x178 QList toolbarIds; loadToolbarsFromFile(actionManager, &path, &toolbarIds); ``` 2. **Toolbars are shown/activated via TULayoutManager::showToolbar**: ```cpp // During setupToolbars (0x14002f840) // This is vtable+416 on TULayoutManager, NOT AC_Manager layoutManager->showToolbar("FileToolbar", true); layoutManager->showToolbar("EditToolbar", true); ``` 3. **View-specific toolbars are looked up via `AC_Manager::toolbarElement()`** (inside the view’s `toolbar()` override): ```cpp // Common pattern in HarmonyPremium.exe view toolbar() overrides: // - AC_Manager* is reachable from a TUWidgetLayoutView-embedded TULayoutView* at [this-0x38] AC_Manager* manager = *(AC_Manager**)((char*)this - 0x38); if (!manager) { return QDomElement(); } return manager->toolbarElement(QString("DrawingViewToolbar")); ``` ### AC_Manager Methods Used The AC_Manager provides these key toolbar methods: | Method | Vtable Offset | Description | |--------|--------------|-------------| | `toolbarElement(name)` | `+0x1A0` | Returns the `` `QDomElement` for `name` (member-function ABI uses a hidden return buffer in `RDX`) | | `loadToolbars(element, outIds)` | `+0x170` | Loads toolbar definitions from a `QDomElement` (same structure as `toolbars.xml`) | | `loadToolbars(path, outIds)` | `+0x178` | Loads toolbar definitions from an XML file (e.g., `toolbars.xml`) | | `createToolbar(name, config, mainWindow, area, identity, owner)` | `+0x180` | Creates an `AC_Toolbar` instance from a stored toolbar id (implementation: `AC_ToolbarImpl`) | | `createToolbar(element, config, mainWindow, area, identity, owner)` | `+0x188` | Creates an `AC_Toolbar` instance from a `QDomElement` (implementation: `AC_ToolbarImpl`) | ### AC_Toolbar Creation / Reuse `TULayoutFrame::showViewToolBar()` does **not** create a new `AC_Toolbar` every time focus changes. Observed behavior (ToonBoomLayout.dll `0x7ffa0be4bb70`): - The frame keeps a single `AC_Toolbar*` at `TULayoutFrame + 0xC0` (`m_viewToolbar`) - On each tab/view change, it: - calls `m_viewToolbar->setOwner(currentView->getWidget())` - calls `m_viewToolbar->changeContent(toolbarEl, buttonConfig, buttonDefaultConfig)` - docks `m_viewToolbar->toQToolBar()` into the frame’s `TULayoutMainWindow` The `AC_Toolbar` object itself is created lazily by `TULayoutFrame::createEmptyToolBar(name)`: - Builds an empty `` element - Calls `AC_Manager::createToolbar(emptyEl, nullptr, m_layoutManager, 4, nameLatin1, nullptr)` - Stores the result as `m_viewToolbar` ### LAY_ToolbarInfo Configuration Each view can have stored toolbar configuration via `LAY_ToolbarInfo`: ```cpp // Structure of LAY_ToolbarInfo (~104 bytes) // Analyzed from ToonBoomLayout.dll constructors and getters class LAY_ToolbarInfo { int m_x; // +0x00 - X position (default: 0) int m_y; // +0x04 - Y position (default: 0) int m_index; // +0x08 - Toolbar index (default: -1) int m_width; // +0x0C - Width (default: -1) int m_height; // +0x10 - Height (default: -1) bool m_newline; // +0x14 - Break to new line (default: false) bool m_visible; // +0x15 - Visibility (default: true) bool m_isDefault; // +0x16 - Default/unconfigured state (Layout hides view-toolbar when true) // padding // +0x17 QString m_name; // +0x18 - Toolbar name (24 bytes with SSO) Qt::Orientation m_orientation; // +0x30 - Orientation (default: Horizontal) Qt::ToolBarArea m_toolBarArea; // +0x34 - Docking area (default: TopToolBarArea=4) QList m_buttonConfig; // +0x38 - Current button order (24 bytes) QList m_buttonDefaultConfig; // +0x50 - Default button order (24 bytes) // Total size: ~104 bytes }; ``` **Key LAY_ToolbarInfo Methods (ToonBoomLayout.dll):** | Method | Address | Description | |--------|---------|-------------| | `LAY_ToolbarInfo()` | `0x7ffa0be6a3a0` | Default constructor | | `LAY_ToolbarInfo(QString, int, int, int, int, int, bool, bool)` | `0x7ffa0be6a2c0` | Initializes `name,x,y,index,width,height,newline,visible` (sets `m_isDefault=1`) | | `getName()` | `0x7ffa0be392b0` | Returns toolbar name | | `getHeight()` | `0x7ffa0be39280` | Returns height | | `isVisible()` | `0x7ffa0be39570` | Returns visibility | | `isDefault()` | `0x7ffa0be39560` | Returns `m_isDefault` (Layout treats non-zero as “hide/reset view-toolbar”) | | `getButtonConfig()` | `0x7ffa0be6a990` | Returns `nullptr` when `m_isDefault!=0`, else `&m_buttonConfig` | | `setButtonConfig()` | `0x7ffa0be6a9a0` | Copies config into `m_buttonConfig` and sets `m_isDefault=0` | | `setButtonDefaultConfig()` | `0x7ffa0be6aaf0` | Copies config into `m_buttonDefaultConfig` | | `setName()` | `0x7ffa0be39bd0` | Sets toolbar name | | `setVisible()` | `0x7ffa0be39c10` | Sets visibility | | `fromXml()` | `0x7ffa0be6a520` | Loads from QDomElement | | `toXml()` | `0x7ffa0be6ab00` | Saves to QDomElement | Note: the `m_buttonDefaultConfig` accessor is ICF-folded with another trivial accessor and may show up in IDA as `?getChildren@TULayoutSplitter@@...` at `0x7ffa0be5ac60`; `TULayoutFrame::showViewToolBar()` uses that address as `&m_buttonDefaultConfig`. ### How `m_toolbarInfo` Gets Initialized (Critical) `TULayoutFrame::showViewToolBar()` will *hide* the view-toolbar when `TULayoutView::m_toolbarInfo.m_isDefault != 0`, so the question becomes: **where does non-default toolbar info come from?** This happens when a view is added to a frame (e.g., via `raiseArea()` / layout activation): - `TULayoutArea::add` (ToonBoomLayout.dll `0x7ffa0be41c10`) first checks the frame’s cache (`getToolbarInfoForView(viewKey)`). - If no cached entry exists, it calls `view->toolbar()` and reads `toolbarEl.attribute("id")` to get the toolbar identity string (e.g., `DrawingViewToolbar`). - It then queries `TULayoutStorage::getViewToolbarConfig(viewKey, wantDefault)` (ToonBoomLayout.dll `0x7ffa0be5cff0`) to fetch button config lists keyed by `viewKey` (typically `view->getCaption(true)`). - If configs exist, it builds a `LAY_ToolbarInfo` with: - `m_name = toolbarId` - `m_buttonConfig` + `m_buttonDefaultConfig` populated (which flips `m_isDefault -> 0`) - applies it via `view->setToolbarInfo(info)` and caches it in the frame. Persisted customization updates the config lists via: - `TULayoutFrame::toolbarWasCustomized` (ToonBoomLayout.dll `0x7ffa0be4be20`) → `TULayoutStorage::setViewToolbarConfig(viewKey, config, isDefault=false)` + `saveLayoutToolbar()`. ## Implementing Custom View Toolbars ### Step 1: Define Toolbar in XML Create a toolbar definition that will be loaded by AC_Manager: ```xml ``` ### Step 2: Override toolbar() in Your View ```cpp class MyCustomView : public TULayoutView { public: QDomElement toolbar() override { // Get AC_Manager - depends on your view's structure. // In HarmonyPremium.exe views derived from TUWidgetLayoutView, AC_Manager* is reachable from the embedded // TULayoutView* at [this-0x38]. For a custom TULayoutView subclass, store AC_Manager* yourself (or fetch // it via PLUG_Services::getActionManager()). AC_Manager* manager = getActionManager(); // your helper if (!manager) { return QDomElement(); } return manager->toolbarElement(QString("MyCustomViewToolbar")); } }; ``` ### Step 3: Register Toolbar XML Ensure your toolbar XML is loaded during initialization: ```cpp // Option 1: Add to existing toolbars.xml (if modifying installation) // Option 2: Load programmatically AC_Manager* manager = PLUG_Services::getActionManager(); if (manager) { // Use AC_Manager::loadToolbars(element, outIds) (vtable+0x170) // Note: loadToolbars expects the same structure as toolbars.xml; easiest is to wrap your inside a root. QDomDocument doc; doc.setContent(myToolbarXml); QDomElement element = doc.documentElement(); QList ids; manager->loadToolbars(element, ids); } ``` ### Step 4: Ensure `LAY_ToolbarInfo` Is Non-Default `TULayoutFrame::showViewToolBar()` (ToonBoomLayout.dll `0x7ffa0be4bb70`) will hide/reset the view-toolbar when `TULayoutView::m_toolbarInfo.m_isDefault != 0`. For a custom view, you must ensure: - `LAY_ToolbarInfo::m_name` is set to the toolbar element’s `"id"` (used as the toolbar instance identity when `createEmptyToolBar(name)` creates `m_viewToolbar`) - `LAY_ToolbarInfo::m_buttonConfig` (and typically `m_buttonDefaultConfig`) are populated, so `m_isDefault` flips to `0` Built-in views typically get this via `TULayoutArea::add()` reading config from `TULayoutStorage` (see the section above). If your view has no stored config, the toolbar will remain hidden until you seed it. Minimal pattern (conceptual): ```cpp LAY_ToolbarInfo info = this->getToolbarInfo(); info.setName(QString("MyCustomViewToolbar")); QList buttons = {/* item ids in desired order */}; info.setButtonConfig(&buttons); // flips m_isDefault -> 0 info.setButtonDefaultConfig(&buttons); // used for reset/customize UI this->setToolbarInfo(info); ``` ## Key Memory Offsets ### TULayoutView (from TULayoutView*) - `+0x00`: vptr - `+0x08`: QString m_internalName - `+0x20`: LAY_ToolbarInfo m_toolbarInfo (104 bytes) - `+0x88`: AC_Menu* m_menuByType[2] - `+0xA0`: QString m_caption ### TULayoutFrame (from TULayoutFrame*) - `+0x28`: TULayoutManager* m_layoutManager - `+0x48`: TULayoutMainWindow* m_mainWindow (toolbar host) - `+0x90`: AC_Toolbar* m_toolbar - `+0xC0`: AC_Toolbar* m_viewToolbar ### AC_ToolbarImpl (from QToolBar base) - `+0x28`: AC_ContainerImpl m_container (88 bytes) - `+0x80`: AC_Toolbar vptr - `+0x98`: QString m_responderIdentity - `+0xC8`: bool m_isCustomizable - `+0xE0`: QObject* m_owner ## Database Reference ### HarmonyPremium.exe | Symbol | Address | Description | |--------|---------|-------------| | TULayoutManager__setupToolbars | `0x14002F840` | Initializes/shows global toolbars by name | | VL_BaseDrawingViewQt__toolbar | `0x1403B9880` | Returns `DrawingViewToolbar` element | | CV_CameraViewQt__toolbar | `0x1403BC450` | Returns `CameraViewToolbar` element | | VL_ModelViewQt__toolbar | `0x1403C8A20` | Returns `ModelViewToolbar` element (Reference View) | | TL_DockWindowQT__toolbar | `0x14011C5F0` | Returns `TimelineViewToolbar` element | ### ToonBoomActionManager.dll | Symbol | Address | Description | |--------|---------|-------------| | AC_ManagerImpl__toolbarElement | `0x180016240` | `AC_Manager::toolbarElement(name)` implementation (returns `QDomElement` by value) | | AC_ToolbarImpl__changeContent_fromElement | `0x1800332F0` | `AC_Toolbar::changeContent(element, config, defaultConfig)` | | AC_ToolbarImpl::create | `0x180033910` | Build toolbar from XML element | | AC_ToolbarImpl::create (with config) | `0x180033D50` | Build toolbar from XML element + config list | ### ToonBoomLayout.dll (Toolbar Infrastructure) | Symbol | Address | Description | |--------|---------|-------------| | TULayoutArea::add | `0x7ffa0be41c10` | Initializes `TULayoutView::m_toolbarInfo` from `view->toolbar().attribute(\"id\")` + `TULayoutStorage` configs | | TULayoutFrame::showViewToolBar | `0x7ffa0be4bb70` | Core view-toolbar update flow (calls view->toolbar(), then `changeContent` + docking) | | TULayoutFrame::createEmptyToolBar | `0x7ffa0be47730` | Lazily creates/clears `m_viewToolbar` | | TULayoutFrame::removeViewToolBar | `0x7ffa0be4a700` | Hides `m_viewToolbar` | | TULayoutFrame::getCurToolbarState | `0x7ffa0be48b20` | Captures geometry/visibility from `m_viewToolbar->toQToolBar()` | | TULayoutFrame::toolbarWasCustomized | `0x7ffa0be4be20` | Persists button order into `TULayoutStorage` (flips `m_isDefault -> 0`) | | TULayoutView::toolbar | `0x7ffa0be610d0` | Base implementation (returns empty `QDomElement`) | | TULayoutView::setToolbarInfo | `0x7ffa0be610c0` | Stores `LAY_ToolbarInfo` into the view | | TULayoutView::getToolbarInfo | `0x7ffa0be60e80` | Returns pointer to embedded `LAY_ToolbarInfo` | | TULayoutStorage::getViewToolbarConfig | `0x7ffa0be5cff0` | Returns stored `QList` pointer for a view-toolbar config | | TULayoutStorage::setViewToolbarConfig | `0x7ffa0be5fa20` | Stores a view-toolbar config list (current or default) | | LAY_ToolbarInfo::setButtonConfig | `0x7ffa0be6a9a0` | Copies config + flips `m_isDefault` | ## Toolbar Names Reference ### Global Toolbars - `FileToolbar` - File operations - `EditToolbar` - Edit operations - `DrawingToolToolbar` - Drawing tools - `ArtLayerToolbar` - Art layer selection - `SceneplanningToolToolbar` - Scene planning tools - `OnionSkinToolbar` - Onion skin controls - `FlipToolbar` - Flip/mirror tools - `GameToolbar` - Game export tools - `AlignmentGuidesToolbar` - Alignment guides - `MarkDrawingToolBar` - Mark drawing tools ### View-Specific Toolbars - `DrawingViewToolbar` - Drawing view - `CameraViewToolbar` - Camera view - `TimelineViewToolbar` - Timeline view - `XsheetViewToolbar` - Xsheet view - `NetworkViewToolbar` - Network/Node view - `FunctionViewToolbar` - Function curve view - `PlaybackViewToolbar` - Playback view - `ModelViewToolbar` - Model view - `LibraryViewToolbar` - Library view - `ModuleLibraryViewToolbar` - Module library view - `FreeViewToolbar` - Free-form view ## Troubleshooting ### Toolbar Not Appearing After raiseArea If you call `TULayoutManager::raiseArea()` and the view's toolbar doesn't appear: 1. **`raiseArea` doesn't trigger toolbars directly** - `raiseArea` only brings a view to the foreground - Toolbar display is handled by `TULayoutFrame::showViewToolBar()` (typically reached via tab/view changes or `TULayoutManager::showViewToolBars()`) 2. **Manual toolbar trigger** ```cpp TULayoutView* view = layoutManager->raiseArea("MyArea", nullptr, true, QPoint(0,0)); if (view) { TULayoutFrame* frame = view->getLayoutFrame(view->getWidget()); if (frame) { // Direct refresh options: frame->showViewToolBar(); layoutManager->showViewToolBars(); } } ``` 3. **Set focus to trigger natural flow** ```cpp TULayoutView* view = layoutManager->raiseArea("MyArea", nullptr, true, QPoint(0,0)); if (view && view->getWidget()) { view->getWidget()->setFocus(Qt::OtherFocusReason); } ``` ### View toolbar() Returns Empty Element If your custom view's `toolbar()` method doesn't work: 1. **Check AC_Manager access** - For `TUWidgetLayoutView` subclasses: `AC_Manager* mgr = TULayoutView_getActionManager(this);` - The manager must be non-null 2. **Verify toolbar is registered** - The toolbar XML must be loaded via `AC_Manager::loadToolbars(path, outIds)` or `loadToolbars(element, outIds)` - Check that the toolbar `id` attribute matches what you're requesting 3. **Debug the vtable call** ```cpp QDomElement MyView::toolbar() { AC_Manager* manager = TULayoutView_getActionManager(this); if (!manager) { qDebug() << "No AC_Manager available"; return QDomElement(); } // NOTE: AC_Manager::toolbarElement(...) returns QDomElement by value. // MSVC x64 member-function ABI uses a hidden return buffer pointer (RDX), // so a raw vtable call must include an explicit QDomElement* out. void** vtable = *reinterpret_cast(manager); using ToolbarElementFn = QDomElement* (__fastcall *)(AC_Manager* self, QDomElement* out, const QString* name); auto toolbarElement = reinterpret_cast(vtable[52]); QDomElement element; QString name("MyViewToolbar"); toolbarElement(manager, &element, &name); if (element.isNull()) { qDebug() << "Toolbar" << name << "not found in AC_Manager"; } return element; } ``` ### Focus/Frame Issues 1. **Frame not set as current** - The system uses `setCurrentLayoutFrame` to track which frame is active - `showViewToolBar()` is triggered by tab/view changes or `TULayoutManager::showViewToolBars()`, not directly by `setCurrentLayoutFrame` 2. **View not in frame's current tab** - The raised view may be in a tab but not the current one - Use `TULayoutFrame::setCurrentTab()` if needed See **Database Reference** for verified addresses used in this document. ### Example: Complete Toolbar Integration ```cpp // 1. During initialization - register your toolbar XML void MyPlugin::initialize(AC_Manager* manager) { QString toolbarXml = R"( )"; QDomDocument doc; doc.setContent(toolbarXml); // Register with AC_Manager (loadToolbars(element, outIds)) QList ids; manager->loadToolbars(doc.documentElement(), ids); } // 2. In your view class - override toolbar() QDomElement MyView::toolbar() { AC_Manager* manager = TULayoutView_getActionManager(this); if (!manager) return QDomElement(); void** vtable = *reinterpret_cast(manager); auto getToolbarElement = reinterpret_cast(vtable[52]); QDomElement element; getToolbarElement(manager, &element, QString("MyViewToolbar")); return element; } // 3. When raising your view and ensuring toolbar appears void showMyView(TULayoutManager* layoutManager) { TULayoutView* view = layoutManager->raiseArea("MyView", nullptr, true, QPoint(0,0)); if (view) { // Ensure toolbar displays by triggering focus flow if (QWidget* widget = view->getWidget()) { widget->setFocus(Qt::OtherFocusReason); } // Or explicitly trigger a refresh TULayoutFrame* frame = view->getLayoutFrame(view->getWidget()); if (frame) { frame->showViewToolBar(); layoutManager->showViewToolBars(); } } } ```