toon-boom-extension-framework/docs/TULayoutView_Toolbar_Integration.md

685 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 views 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<QString> toolbarIds;
actionManager->loadToolbars(toolbarsPath.ToQString(), toolbarIds);
```
The `toolbars.xml` file defines toolbar content using XML elements like:
```xml
<toolbar id="DrawingViewToolbar" trContext="Toolbars">
<item id="DrawingTool.Select" />
<separator />
<item id="DrawingTool.Brush" />
<!-- ... more items ... -->
</toolbar>
```
### 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 Booms “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 frames 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 views 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 `<toolbar>` 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<void***>(actionManager);
using LoadToolbarsFromFileFn = void (__fastcall *)(AC_Manager* self, const QString* path, QList<QString>* outIds);
auto loadToolbarsFromFile = reinterpret_cast<LoadToolbarsFromFileFn>(vtable[47]); // vtable+0x178
QList<QString> 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 views `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 `<toolbar ...>` `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 frames `TULayoutMainWindow`
The `AC_Toolbar` object itself is created lazily by `TULayoutFrame::createEmptyToolBar(name)`:
- Builds an empty `<toolbar/>` 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<QString> m_buttonConfig; // +0x38 - Current button order (24 bytes)
QList<QString> 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 frames 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
<toolbar id="MyCustomViewToolbar" trContext="MyToolbars">
<item id="MyResponder.Action1" />
<item id="MyResponder.Action2" />
<separator />
<placeholder id="CustomItems" />
</toolbar>
```
### 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 <toolbar> inside a <toolbars> root.
QDomDocument doc;
doc.setContent(myToolbarXml);
QDomElement element = doc.documentElement();
QList<QString> 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 elements `"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<QString> 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<QString>` 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<void***>(manager);
using ToolbarElementFn = QDomElement* (__fastcall *)(AC_Manager* self, QDomElement* out, const QString* name);
auto toolbarElement = reinterpret_cast<ToolbarElementFn>(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"(
<toolbar id="MyViewToolbar" trContext="MyPlugin">
<item id="MyResponder.DoSomething" />
<separator />
<item id="MyResponder.DoOther" />
</toolbar>
)";
QDomDocument doc;
doc.setContent(toolbarXml);
// Register with AC_Manager (loadToolbars(element, outIds))
QList<QString> 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<void***>(manager);
auto getToolbarElement = reinterpret_cast<void(*)(AC_Manager*, QDomElement*, const QString&)>(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();
}
}
}
```