[Scummvm-git-logs] scummvm master -> c3adec8c897c97a79fde781a3ca3d5246ed5b8a0
sev-
noreply at scummvm.org
Thu Feb 26 23:54:12 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
c3adec8c89 DIRECTOR: DT: Change Score window's GUI for Director Engine debugger
Commit: c3adec8c897c97a79fde781a3ca3d5246ed5b8a0
https://github.com/scummvm/scummvm/commit/c3adec8c897c97a79fde781a3ca3d5246ed5b8a0
Author: ramyak-sharma (76775021+ramyak-sharma at users.noreply.github.com)
Date: 2026-02-27T00:54:08+01:00
Commit Message:
DIRECTOR: DT: Change Score window's GUI for Director Engine debugger
Changed paths:
engines/director/debugger/dt-internal.h
engines/director/debugger/dt-score.cpp
diff --git a/engines/director/debugger/dt-internal.h b/engines/director/debugger/dt-internal.h
index 9caab66fb13..36476ba5733 100644
--- a/engines/director/debugger/dt-internal.h
+++ b/engines/director/debugger/dt-internal.h
@@ -110,6 +110,29 @@ typedef struct WindowFlag {
} WindowFlag;
typedef struct ImGuiState {
+
+ struct ScoreConfig {
+ float _sidebarWidth = 60.0f;
+ float _cellWidth = 16.0f;
+ float _cellHeight = 20.0f;
+ int _visibleFrames = 60;
+ int _visibleChannels = 25;
+ float _tableWidth = _cellWidth * _visibleFrames;
+ float _tableHeight = _cellHeight * _visibleChannels;
+ float _rulerHeight = 30.0f;
+ float _rulerWidth = _cellWidth * _visibleFrames;
+ float _sidebar1Height = _cellHeight * 6;
+ float _cellHeightExtended = 5 * _cellHeight;
+ ImU32 _tableLightColor = IM_COL32(51, 51, 51, 255);
+ ImU32 _tableDarkColor = IM_COL32(38, 38, 38, 255);
+ ImU32 _borderColor = IM_COL32(102, 102, 102, 100);
+ } _scoreCfg;
+
+ struct ScoreState {
+ int xSliderValue = 1; // first visible frame, 1 indexed
+ int channelScrollOffset = 1; // first visible channel, 1 indexed
+ } _scoreState;
+
struct {
Common::HashMap<CastMember *, ImGuiImage> _textures;
bool _listView = true;
@@ -117,6 +140,7 @@ typedef struct ImGuiState {
ImGuiTextFilter _nameFilter;
int _typeFilter = 0x7FFF;
} _cast;
+
struct {
ImGuiTextFilter _nameFilter;
bool _showScriptContexts = true;
@@ -139,13 +163,15 @@ typedef struct ImGuiState {
ImVec4 _channel_toggle = ImColor(IM_COL32(0x30, 0x30, 0xFF, 0xFF));
ImVec4 _current_statement = ImColor(IM_COL32(0xFF, 0xFF, 0x00, 0xFF));
- ImVec4 _line_color = ImVec4(0.44f, 0.44f, 0.44f, 1.0f);
+ //ImVec4 _line_color = ImVec4(0.44f, 0.44f, 0.44f, 1.0f);
+ ImVec4 _line_color = ImColor(IM_COL32(0x2F, 0x2F, 0x2F, 0xFF)); // added for better contrast
ImVec4 _call_color = ImColor(IM_COL32(0xFF, 0xC5, 0x5C, 0xFF));
ImVec4 _builtin_color = ImColor(IM_COL32(0x60, 0x7C, 0xFF, 0xFF));
ImVec4 _var_color = ImColor(IM_COL32(0x4B, 0xCD, 0x5E, 0xFF));
ImVec4 _literal_color = ImColor(IM_COL32(0xFF, 0x9F, 0xDA, 0x9E));
ImVec4 _comment_color = ImColor(IM_COL32(0xFF, 0xA5, 0x9D, 0x95));
- ImVec4 _type_color = ImColor(IM_COL32(0x13, 0xC5, 0xF9, 0xFF));
+ //ImVec4 _type_color = ImColor(IM_COL32(0x13, 0xC5, 0xF9, 0xFF));
+ ImVec4 _type_color = ImColor(IM_COL32(0xB8, 0xB8, 0xB8, 0xC0)); // added this instead because better contrast
ImVec4 _keyword_color = ImColor(IM_COL32(0xC1, 0xC1, 0xC1, 0xFF));
ImVec4 _the_color = ImColor(IM_COL32(0xFF, 0x49, 0xEF, 0xFF));
@@ -165,8 +191,8 @@ typedef struct ImGuiState {
ImColor(IM_COL32(0xff, 0xce, 0x9c, 0x80)), // 0xffce9c,
};
- ImColor _channel_selected_col = ImColor(IM_COL32(0x94, 0x00, 0xD3, 0xFF));
- ImColor _channel_hovered_col = ImColor(IM_COL32(0xFF, 0xFF, 0, 0x3C));
+ ImColor _channelSelectedCol = ImColor(IM_COL32(0x94, 0x00, 0xD3, 0xFF));
+ ImColor _channelHoveredCol = ImColor(IM_COL32(0xFF, 0xFF, 0, 0x3C));
int _contColorIndex = 0;
} _colors;
@@ -194,6 +220,7 @@ typedef struct ImGuiState {
struct {
int frame = -1;
int channel = -1;
+ bool isMainChannel = false;
} _selectedScoreCast;
struct {
@@ -247,7 +274,7 @@ ImColor brightenColor(const ImColor &color, float factor);
Window *windowListCombo(Common::String *target);
Common::String formatHandlerName(int scriptId, int castId, Common::String handlerName, ScriptType scriptType, bool childScript);
-void showCast(); // dt-cast.cpp
+void showCast(); // dt-cast.cpp
void showControlPanel(); // dt-controlpanel.cpp
// dt-lists.cpp
@@ -260,8 +287,8 @@ void showArchive();
void showScore();
void showChannels();
-void renderOldScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo); // dt-script-d2.cpp
-void renderScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo); // dt-script-d4.cpp
+void renderOldScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo); // dt-script-d2.cpp
+void renderScriptAST(ImGuiScript &script, bool showByteCode, bool scrollTo); // dt-script-d4.cpp
// dt-scripts.cpp
void showFuncList();
diff --git a/engines/director/debugger/dt-score.cpp b/engines/director/debugger/dt-score.cpp
index 2fdeac58b9c..b74690fe29c 100644
--- a/engines/director/debugger/dt-score.cpp
+++ b/engines/director/debugger/dt-score.cpp
@@ -34,22 +34,58 @@
#include "director/sprite.h"
#include "director/window.h"
+
namespace Director {
namespace DT {
-enum { kModeMember, kModeBehavior, kModeLocation, kModeInk, kModeBlend, kModeExtended,
- kChTempo, kChPalette, kChTransition, kChSound1, kChSound2, kChScript };
+enum { kModeMember, kModeBehavior, kModeLocation, kModeInk, kModeBlend, kModeExtended};
+enum { kChTempo, kChPalette, kChTransition, kChSound1, kChSound2, kChScript };
const char *modes[] = { "Member", "Behavior", "Location", "Ink", "Blend", "Extended" };
const char *modes2[] = {
ICON_MS_TIMER, "Tempo", // timer
ICON_MS_PALETTE, "Palette", // palette
ICON_MS_TRANSITION_FADE, "Transition", // transition_fade
ICON_MS_VOLUME_UP,"Sound 1", // volume_up
- ICON_MS_VOLUME_DOWN,"Sound 2", // volume_up
+ ICON_MS_VOLUME_DOWN,"Sound 2", // volume_down
ICON_MS_FORMS_APPS_SCRIPT, "Script", // forms_apps_script
};
-#define FRAME_PAGE_SIZE 100
+struct ScoreLayout {
+ ImVec2 sidebar1Pos;
+ ImVec2 mainChannelGridPos;
+ ImVec2 modeSelectorPos;
+ ImVec2 rulerPos;
+ ImVec2 sidebar2Pos;
+ ImVec2 gridPos;
+ ImVec2 sliderPos;
+ ImVec2 sliderYPos;
+};
+
+static ScoreLayout computeLayout(ImVec2 origin, const ImGuiState::ScoreConfig &cfg) {
+ ScoreLayout l;
+ l.sidebar1Pos = ImVec2(origin.x, origin.y);
+ l.mainChannelGridPos = ImVec2(origin.x + cfg._sidebarWidth, origin.y);
+ l.modeSelectorPos = ImVec2(origin.x, origin.y + cfg._sidebar1Height);
+ l.rulerPos = ImVec2(origin.x + cfg._sidebarWidth, origin.y + cfg._sidebar1Height);
+ l.sidebar2Pos = ImVec2(origin.x, origin.y + cfg._sidebar1Height + cfg._rulerHeight);
+ l.gridPos = ImVec2(origin.x + cfg._sidebarWidth, origin.y + cfg._sidebar1Height + cfg._rulerHeight);
+ l.sliderPos = ImVec2(origin.x + cfg._sidebarWidth, origin.y + cfg._sidebar1Height + cfg._rulerHeight + cfg._tableHeight + 8.0f);
+ l.sliderYPos = ImVec2(origin.x + cfg._sidebarWidth + cfg._tableWidth + 8.0f, origin.y + cfg._sidebar1Height + cfg._rulerHeight);
+ return l;
+}
+
+// helper to convert color for drawlist
+static inline ImU32 U32(const ImVec4 &c) {
+ return ImGui::GetColorU32(c);
+}
+
+// helper to draw thin rectangles for table grid
+static void addThinRect(ImDrawList *dl, ImVec2 min, ImVec2 max, ImU32 col, float thickness = 0.1f) {
+ dl->AddLine(ImVec2(min.x, min.y), ImVec2(max.x, min.y), col, thickness); // top
+ dl->AddLine(ImVec2(max.x, min.y), ImVec2(max.x, max.y), col, thickness); // right
+ dl->AddLine(ImVec2(max.x, max.y), ImVec2(min.x, max.y), col, thickness); // bottom
+ dl->AddLine(ImVec2(min.x, max.y), ImVec2(min.x, min.y), col, thickness); // left
+}
static void buildContinuationData(Window *window) {
if (_state->_loadedContinuationData == window->getCurrentMovie()->getMacName()) {
@@ -93,8 +129,8 @@ static void buildContinuationData(Window *window) {
prevSprite->_spriteType == sprite._spriteType,
prevSprite->_castId == sprite._castId,
prevSprite->_startPoint == sprite._startPoint,
- prevSprite->_width == sprite._width,
- prevSprite->_height == sprite._height,
+ prevSprite->Width == sprite.Width,
+ prevSprite->Height == sprite.Height,
prevSprite->_ink == sprite._ink,
prevSprite->_foreColor == sprite._foreColor,
prevSprite->_backColor == sprite._backColor,
@@ -128,596 +164,755 @@ static void buildContinuationData(Window *window) {
_state->_loadedContinuationData = window->getCurrentMovie()->getMacName();
}
-static void displayScoreChannel(int ch, int mode, int modeSel, Window *window) {
- Score *score = window->getCurrentMovie()->getScore();
- uint numFrames = score->_scoreCache.size();
-
- const uint currentFrameNum = score->getCurrentFrameNum();
- const ImU32 cell_bg_color = ImGui::GetColorU32(ImVec4(0.7f, 0.7f, 0.0f, 0.65f));
- ImGui::TableNextRow();
+static void drawSliderY(ImVec2 pos, int numChannels) {
+ auto &cfg = _state->_scoreCfg;
+ ImGui::SetCursorScreenPos(pos);
+ ImGui::SetNextItemWidth(16.0f);
+ int visibleChannels = (_state->_scoreMode == kModeExtended) ? (int)(cfg._tableHeight / cfg._cellHeightExtended) : cfg._visibleChannels;
+ int maxScroll = MAX(1, numChannels - visibleChannels);
+ ImGui::VSliderInt("##channelSlider", ImVec2(16.0f, cfg._tableHeight), &_state->_scoreState.channelScrollOffset, maxScroll, 1);
+}
- ImGui::PushFont(_state->_tinyFont);
+static void drawSidebar1(ImDrawList *dl, ImVec2 startPos, Score *score) {
+ float toggleColWidth = 20.0f;
+ float labelColWidth = 40.0f;
+ float totalWidth = toggleColWidth + labelColWidth;
+ auto &cfg = _state->_scoreCfg;
- if (modeSel == kModeExtended && mode == kModeExtended)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_TableRowBgAlt));
+ for (int ch = 1; ch <= 6; ch++) {
+ float y = startPos.y + (ch - 1) * cfg._cellHeight;
+ ImVec2 rowMin = ImVec2(startPos.x, y);
+ ImVec2 rowMax = ImVec2(startPos.x + totalWidth, y + cfg._cellHeight);
- { // Playback toggle
- ImGui::TableNextColumn();
+ dl->AddRectFilled(rowMin, rowMax, cfg._tableDarkColor);
+ addThinRect(dl, rowMin, rowMax, cfg._borderColor);
- ImGui::PushID(ch + 20000 - mode);
- ImDrawList *dl = ImGui::GetWindowDrawList();
- const ImVec2 pos = ImGui::GetCursorScreenPos();
- const ImVec2 mid(pos.x + 7, pos.y + 7);
+ float radius = 5.0f;
+ float pad = cfg._cellHeight * 0.12f; // inner padding
- ImGui::InvisibleButton("Line", ImVec2(16, ImGui::GetFontSize()));
- ImGui::SetItemTooltip("Playback toggle");
+ ImVec2 center(rowMin.x + pad + radius, rowMax.y - pad - radius);
- if (ImGui::IsItemClicked(0)) {
- if (mode == kModeMember) {
- score->_channels[ch]->_visible = !score->_channels[ch]->_visible;
+ dl->AddCircleFilled(center, radius, U32(_state->_colors._channel_toggle));
- window->render(true);
- }
- }
+ // channel number centered in the right column
+ ImFont *iconFont = ImGui::GetIO().FontDefault;
+ const char *icon = modes2[(ch - 1) * 2];
+ float textlen = ImGui::CalcTextSize(icon).x;
+ float textX = startPos.x + toggleColWidth + (labelColWidth - textlen) / 2.0f;
+ float textY = y + (cfg._cellHeight - ImGui::GetTextLineHeight()) / 2.0f;
+ dl->AddText(iconFont, 0.0f, ImVec2(textX, textY), U32(_state->_colors._type_color), icon);
- if (mode != kModeMember || score->_channels[ch]->_visible)
- dl->AddCircleFilled(mid, 4.0f, ImColor(_state->_colors._channel_toggle));
- else
- dl->AddCircle(mid, 4.0f, ImColor(_state->_colors._channel_toggle));
+ // invisible button covering the row for interaction
+ ImGui::SetCursorScreenPos(rowMin);
+ ImGui::InvisibleButton(Common::String::format("##s1row%d", ch).c_str(), ImVec2(totalWidth, cfg._cellHeight));
+ if (ImGui::IsItemHovered())
+ ImGui::SetTooltip("%s", modes2[(ch - 1) * 2 + 1]);
- ImGui::PopID();
}
+}
- { // Channel name / number
- ImGui::TableNextColumn();
+static void drawSidebar2(ImDrawList *dl, ImVec2 startPos, Score *score) {
+ float toggleColWidth = 20.0f;
+ float labelColWidth = 40.0f;
+ float totalWidth = toggleColWidth + labelColWidth;
+ auto &cfg = _state->_scoreCfg;
+ float cellH = (_state->_scoreMode == kModeExtended) ? cfg._cellHeightExtended : cfg._cellHeight; // using this only for cell height, as only cell height changes in extended mode
+ int visibleChannels = (_state->_scoreMode == kModeExtended) ? (int)(cfg._tableHeight / cfg._cellHeightExtended) : cfg._visibleChannels;
- float indentSize = 17.0f;
- if (mode < kChTempo && modeSel == kModeExtended)
- indentSize = 10.0f;
+ for (int i = 0; i < visibleChannels; i++) {
+ int ch = i + _state->_scoreState.channelScrollOffset;
+ float y = startPos.y + i * cellH;
+ ImVec2 rowMin = ImVec2(startPos.x, y);
+ ImVec2 rowMax = ImVec2(startPos.x + totalWidth, y + cellH);
- if (modeSel == kModeExtended && mode == kModeExtended)
- indentSize = 0.1f;
+ if (ch >= (int)score->_channels.size()) break;
- ImGui::Indent(indentSize);
- if (mode >= kChTempo) {
- ImGui::PushFont(ImGui::GetIO().FontDefault);
+ dl->AddRectFilled(rowMin, rowMax, cfg._tableDarkColor);
+ addThinRect(dl, rowMin, rowMax, cfg._borderColor);
- ImGui::Text(modes2[(mode - kChTempo) * 2]);
- ImGui::SetItemTooltip(modes2[(mode - kChTempo) * 2 + 1]);
+ // toggle circle
+ // small square at bottom-left of the cell (size relative to cell)
+ float radius = 5.0f; // square size
+ float pad = cfg._cellHeight * 0.12f; // inner padding
- ImGui::PopFont();
- } else if (modeSel != kModeExtended || mode == kModeExtended) {
- ImGui::Text("%3d", ch);
- } else {
- ImGui::Text(modes[mode]);
- }
+ ImVec2 center(rowMin.x + pad + radius, rowMax.y - pad - radius);
- if (ch == _state->_selectedScoreCast.channel)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(0.5f, 0.5f, 0.5f, 0.6f)));
+ if (score->_channels[ch]->_visible)
+ dl->AddCircleFilled(center, radius, U32(_state->_colors._channel_toggle));
else
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImGuiCol_TableHeaderBg));
-
- ImGui::Unindent(indentSize);
- }
-
- numFrames -= _state->_scoreFrameOffset - 1;
- numFrames = MIN<uint>(numFrames, kMaxColumnsInTable - 2);
-
- for (int f = 0; f < (int)numFrames; f++) {
- int rf = f + _state->_scoreFrameOffset - 1;
- Frame &frame = *score->_scoreCache[rf];
- Sprite &sprite = *frame._sprites[ch];
-
- _state->_colors._contColorIndex = frame._sprites[ch]->_colorcode & 0x07;
- if (_state->_colors._contColorIndex > 5)
- _state->_colors._contColorIndex = 0;
-
- ImGui::TableNextColumn();
-
- int startCont = _state->_continuationData[ch][rf].first;
- int endCont = _state->_continuationData[ch][rf].second;
-
- if (!(startCont == endCont) && (sprite._castId.member || sprite.isQDShape())) {
- if (_state->_selectedScoreCast.frame + _state->_scoreFrameOffset - 1 >= startCont &&
- _state->_selectedScoreCast.frame + _state->_scoreFrameOffset - 1 <= endCont &&
- ch == _state->_selectedScoreCast.channel &&
- mode <= kModeExtended) {
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, _state->_colors._channel_selected_col);
- } else if (_state->_hoveredScoreCast.frame >= startCont &&
- _state->_hoveredScoreCast.frame <= endCont &&
- ch == _state->_hoveredScoreCast.channel) {
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, _state->_colors._channel_hovered_col);
- } else {
- if (mode == modeSel)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, _state->_colors._contColors[_state->_colors._contColorIndex]);
- else
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, brightenColor(_state->_colors._contColors[_state->_colors._contColorIndex], 1.5));
- }
+ dl->AddCircle(center, radius, U32(_state->_colors._channel_toggle));
+
+ ImGui::SetCursorScreenPos(rowMin);
+ ImGui::InvisibleButton(Common::String::format("##s2toggle%d", ch).c_str(), ImVec2(toggleColWidth, cellH));
+ if (ImGui::IsItemHovered())
+ ImGui::SetTooltip("Playback toggle");
+ if (ImGui::IsItemClicked()) { // determines what happens on toggle of the button
+ score->_channels[ch]->_visible = !score->_channels[ch]->_visible;
}
- if (rf + 1 == (int)currentFrameNum)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, cell_bg_color);
-
- if (f == _state->_selectedScoreCast.frame + _state->_scoreFrameOffset - 1 &&
- ch == _state->_selectedScoreCast.channel && mode <= kModeExtended)
- ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, ImGui::GetColorU32(ImVec4(1.0f, 0.3f, 0.3f, 0.6f)));
-
- int mode1 = mode;
-
- ImGui::PushID((ch * 20 + mode) * 10000 + f);
-
- // If the frame is not the start, then don't render any text
- if (mode == kModeMember) {
- if (rf != startCont || !(sprite._castId.member || sprite.isQDShape())) {
- if (rf == endCont && sprite._castId.member && mode == _state->_scoreMode) {
- ImGui::PushFont(ImGui::GetIO().FontDefault);
- ImGui::TextUnformatted("\uf819");
- ImGui::PopFont();
- } else {
- if (sprite._castId.member) {
- ImGui::Selectable("â");
- } else {
- ImGui::Selectable(" ");
- }
- }
- mode1 = -1; // Skip cell data rendering
+ // channel num and extra stuff if extended mode
+ char buf[8];
+ snprintf(buf, sizeof(buf), "%d", ch);
+ float textX = startPos.x + toggleColWidth;
+
+ if (_state->_scoreMode != kModeExtended) {
+ float textY = y + (cellH - ImGui::GetTextLineHeight()) / 2.0f;
+ dl->AddText(ImVec2(textX, textY), U32(_state->_colors._keyword_color), buf);
+ } else { // draw channel number and labels
+ dl->AddText(ImVec2(textX, y + 2.0f), U32(_state->_colors._keyword_color), buf);
+ const char *subLabels[] = { "Member", "Behavior", "Ink", "Blend", "Location" };
+ float lineH = ImGui::GetTextLineHeight();
+ for (int s = 0; s < 5; s++) {
+ float subX = textX - 17.0f;
+ float subY = y + lineH + 2.0f + s * lineH; // offset below channel number
+ dl->AddText(ImVec2(subX, subY), U32(_state->_colors._type_color), subLabels[s]);
}
}
+ }
+}
- switch (mode1) {
- case -1:
- break;
-
- case kModeMember:
- if (sprite._castId.member)
- ImGui::Selectable(Common::String::format("%d", sprite._castId.member).c_str());
- else if (sprite.isQDShape())
- ImGui::Selectable("Q");
- else
- ImGui::Selectable(" ");
- break;
-
- case kModeInk:
- ImGui::Selectable(Common::String::format("%s", inkType2str(sprite._ink)).c_str());
- break;
-
- case kModeLocation:
- ImGui::Selectable(Common::String::format("%d, %d", sprite._startPoint.x, sprite._startPoint.y).c_str());
- break;
-
- case kModeBlend:
- ImGui::Selectable(Common::String::format("%d", sprite._blendAmount).c_str());
- break;
-
- case kModeBehavior:
- displayScriptRef(sprite._scriptId);
- break;
-
- case kChTempo:
- if (frame._mainChannels.tempo)
- ImGui::Selectable(Common::String::format("%d", frame._mainChannels.tempo).c_str());
- break;
-
- case kChPalette:
- if (frame._mainChannels.palette.paletteId.member)
- ImGui::Selectable(Common::String::format("%d", frame._mainChannels.palette.paletteId.member).c_str());
- break;
-
- case kChTransition:
- if (frame._mainChannels.transType)
- ImGui::Selectable(Common::String::format("%d", frame._mainChannels.transType).c_str());
- break;
-
- case kChSound1:
- if (frame._mainChannels.sound1.member)
- ImGui::Selectable(Common::String::format("%d", frame._mainChannels.sound1.member).c_str());
- break;
-
- case kChSound2:
- if (frame._mainChannels.sound2.member)
- ImGui::Selectable(Common::String::format("%d", frame._mainChannels.sound2.member).c_str());
- break;
-
- case kChScript:
- displayScriptRef(frame._mainChannels.actionId);
- break;
-
- case kModeExtended: // Render empty row
- default:
- ImGui::Selectable(" ");
+static void drawRuler(ImDrawList *dl, ImVec2 startPos) {
+ auto &cfg = _state->_scoreCfg;
+ int start = _state->_scoreState.xSliderValue;
+ ImVec2 p1 = startPos;
+ ImVec2 p2 = {p1.x + cfg._rulerWidth, p1.y + cfg._rulerHeight};
+
+ dl->AddRectFilled(p1, p2, cfg._tableDarkColor);
+ addThinRect(dl, p1, p2, cfg._borderColor);
+
+ float bigTickLen = cfg._rulerHeight * 0.4f;
+ float smallTickLen = cfg._rulerHeight * 0.3f;
+
+ // i is the real frame number (1-indexed), rulerX is its pixel position
+ for (int i = start; i < start + cfg._visibleFrames; i++) {
+ float rulerX = p1.x + (i - start) * cfg._cellWidth + cfg._cellWidth / 2.0f;
+ float len = smallTickLen;
+ float thickness = 1.0f;
+
+ if (i % 5 == 0) {
+ len = bigTickLen;
+ thickness = 1.5f;
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d", i);
+ float textlen = ImGui::CalcTextSize(buf).x;
+ dl->AddText(ImVec2(rulerX - textlen / 2, p1.y + 4.0), U32(_state->_colors._type_color), buf);
}
- ImGui::PopID();
-
- if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
- _state->_selectedScoreCast.frame = f + _state->_scoreFrameOffset - 1;
- _state->_selectedScoreCast.channel = ch;
-
- if (f + _state->_scoreFrameOffset == (int)currentFrameNum) {
- if (_state->_selectedChannel == ch)
- _state->_selectedChannel = -1;
- else
- _state->_selectedChannel = ch;
-
- window->render(true);
- }
- }
+ dl->AddLine(ImVec2(rulerX, p2.y), ImVec2(rulerX, p2.y - len), U32(_state->_colors._line_color), thickness);
+ }
+}
- if (ImGui::IsItemHovered()) {
- _state->_hoveredScoreCast.frame = f;
- _state->_hoveredScoreCast.channel = ch;
+static void drawModeSelector(ImVec2 startPos) {
+ auto &cfg = _state->_scoreCfg;
+ ImGui::SetNextItemWidth(cfg._sidebarWidth);
+ ImGui::SetCursorScreenPos(startPos);
+ if (ImGui::BeginCombo("##mode", modes[_state->_scoreMode])) {
+ for (int i = 0; i < IM_ARRAYSIZE(modes); i++) {
+ bool isSelected = (_state->_scoreMode == i);
+ if (ImGui::Selectable(modes[i], isSelected))
+ _state->_scoreMode = i;
+ if (isSelected)
+ ImGui::SetItemDefaultFocus();
}
+ ImGui::EndCombo();
}
-
- ImGui::PopFont();
}
-void showScore() {
- if (!_state->_w.score)
- return;
+static void drawSpriteInspector(Score *score, Cast *cast, uint numFrames) {
- ImVec2 pos(40, 40);
- ImGui::SetNextWindowPos(pos, ImGuiCond_FirstUseEver);
+ if (_state->_scoreFrameOffset >= (int)numFrames)
+ _state->_scoreFrameOffset = 1;
- ImVec2 windowSize = ImGui::GetMainViewport()->Size - pos - pos;
- ImGui::SetNextWindowSize(windowSize, ImGuiCond_FirstUseEver);
+ { // Render sprite details
+ Sprite *sprite = nullptr;
+ CastMember *castMember = nullptr;
+ bool shape = false;
- if (ImGui::Begin("Score", &_state->_w.score)) {
- Window *selectedWindow = windowListCombo(&_state->_scoreWindow);
+ if (_state->_selectedScoreCast.frame != -1)
+ sprite = score->_scoreCache[_state->_selectedScoreCast.frame]->_sprites[_state->_selectedScoreCast.channel];
- buildContinuationData(selectedWindow);
+ if (sprite) {
+ castMember = cast->getCastMember(sprite->_castId.member, true);
- Score *score = selectedWindow->getCurrentMovie()->getScore();
- uint numFrames = score->_scoreCache.size();
- Cast *cast = selectedWindow->getCurrentMovie()->getCast();
+ shape = sprite->isQDShape();
+ }
- if (!numFrames) {
- ImGui::Text("No frames");
- ImGui::End();
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Image", ImVec2(200.0f, 70.0f));
+
+ if (castMember || shape) {
+ ImGuiImage imgID = {};
+
+ if (castMember) {
+ switch (castMember->_type) {
+ case kCastBitmap:
+ imgID = getImageID(castMember);
+ break;
+ case kCastShape:
+ imgID = getShapeID(castMember);
+ break;
+ case kCastText:
+ case kCastButton:
+ case kCastRichText:
+ imgID = getTextID(castMember);
+ break;
+
+ default:
+ break;
+ }
+ }
- return;
+ if (castMember && imgID.id) {
+ Common::String name(getDisplayName(castMember));
+ showImage(imgID, name.c_str(), 32.f);
+ } else {
+ ImGui::InvisibleButton("##canvas", ImVec2(32.f, 32.f));
+ }
+ ImGui::SameLine();
+ ImGui::Text("%s", sprite->_castId.asString().c_str());
+ ImGui::Text("%s", spriteType2str(sprite->_spriteType));
}
- if (_state->_selectedScoreCast.frame >= (int)numFrames)
- _state->_selectedScoreCast.frame = 0;
-
- if (!numFrames || _state->_selectedScoreCast.channel >= (int)score->_scoreCache[0]->_sprites.size())
- _state->_selectedScoreCast.channel = 0;
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- if (_state->_scoreFrameOffset >= (int)numFrames)
- _state->_scoreFrameOffset = 1;
+ ImGui::SameLine();
+ ImGui::BeginChild("Details", ImVec2(500.0f, 70.0f));
- { // Render sprite details
- Sprite *sprite = nullptr;
- CastMember *castMember = nullptr;
- bool shape = false;
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Ink", ImVec2(150.0f, 20.0f));
- if (_state->_selectedScoreCast.frame != -1)
- sprite = score->_scoreCache[_state->_selectedScoreCast.frame]->_sprites[_state->_selectedScoreCast.channel];
+ if (castMember || shape) {
+ ImGui::Text("%s", inkType2str(sprite->_ink));
+ ImGui::SameLine(70);
+ ImGui::SetItemTooltip("Ink");
+ ImGui::Text("|");
+ ImGui::SameLine();
+ ImGui::Text("%d", sprite->_blendAmount);
+ ImGui::SameLine();
+ ImGui::SetItemTooltip("Blend");
+ }
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- if (sprite) {
- castMember = cast->getCastMember(sprite->_castId.member, true);
+ ImGui::SameLine();
- shape = sprite->isQDShape();
- }
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Range", ImVec2(100.0f, 20.0f));
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Image", ImVec2(200.0f, 70.0f));
-
- if (castMember || shape) {
- ImGuiImage imgID = {};
-
- if (castMember) {
- switch (castMember->_type) {
- case kCastBitmap:
- imgID = getImageID(castMember);
- break;
- case kCastShape:
- imgID = getShapeID(castMember);
- break;
- case kCastText:
- case kCastButton:
- case kCastRichText:
- imgID = getTextID(castMember);
- break;
-
- default:
- break;
- }
- }
+ if (castMember || shape) {
+ ImGui::TextUnformatted("\uf816"); ImGui::SameLine(); // line_start_circle
+ // the continuation data is 0-indexed but the frames are 1-indexed
+ ImGui::Text("%4d", _state->_continuationData[_state->_selectedScoreCast.channel][_state->_selectedScoreCast.frame].first + 1); ImGui::SameLine(50);
+ ImGui::SetItemTooltip("Start Frame");
+ ImGui::TextUnformatted("\uf819"); ImGui::SameLine(); // line_end_square
+ // the continuation data is 0-indexed but the frames are 1-indexed
+ ImGui::Text("%4d", _state->_continuationData[_state->_selectedScoreCast.channel][_state->_selectedScoreCast.frame].second + 1); ImGui::SameLine();
+ ImGui::SetItemTooltip("End Frame");
+ }
- if (castMember && imgID.id) {
- Common::String name(getDisplayName(castMember));
- showImage(imgID, name.c_str(), 32.f);
- } else {
- ImGui::InvisibleButton("##canvas", ImVec2(32.f, 32.f));
- }
- ImGui::SameLine();
- ImGui::Text("%s", sprite->_castId.asString().c_str());
- ImGui::Text("%s", spriteType2str(sprite->_spriteType));
- }
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- ImGui::PopStyleColor();
- ImGui::EndChild();
+ ImGui::SameLine();
- ImGui::SameLine();
- ImGui::BeginChild("Details", ImVec2(500.0f, 70.0f));
-
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Ink", ImVec2(150.0f, 20.0f));
-
- if (castMember || shape) {
- ImGui::Text("%s", inkType2str(sprite->_ink));
- ImGui::SameLine(70);
- ImGui::SetItemTooltip("Ink");
- ImGui::Text("|");
- ImGui::SameLine();
- ImGui::Text("%d", sprite->_blendAmount);
- ImGui::SameLine();
- ImGui::SetItemTooltip("Blend");
- }
- ImGui::PopStyleColor();
- ImGui::EndChild();
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Flags", ImVec2(200.0f, 20.0f));
- ImGui::SameLine();
+ if (castMember || shape) {
+ ImGui::Checkbox(ICON_MS_LOCK, &sprite->_enabled); ImGui::SameLine();
+ ImGui::SetItemTooltip("enabled");
+ ImGui::Checkbox(ICON_MS_EDIT_NOTE, &sprite->_editable); ImGui::SameLine();
+ ImGui::SetItemTooltip("editable");
+ ImGui::Checkbox(ICON_MS_MOVE_SELECTION_RIGHT, &sprite->_moveable); ImGui::SameLine();
+ ImGui::SetItemTooltip("moveable");
+ ImGui::Checkbox(ICON_MS_DYNAMIC_FEED, &sprite->_trails);
+ ImGui::SetItemTooltip("trails");
+ }
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Range", ImVec2(100.0f, 20.0f));
-
- if (castMember || shape) {
- ImGui::TextUnformatted("\uf816"); ImGui::SameLine(); // line_start_circle
- // the continuation data is 0-indexed but the frames are 1-indexed
- ImGui::Text("%4d", _state->_continuationData[_state->_selectedScoreCast.channel][_state->_selectedScoreCast.frame].first + 1); ImGui::SameLine(50);
- ImGui::SetItemTooltip("Start Frame");
- ImGui::TextUnformatted("\uf819"); ImGui::SameLine(); // line_end_square
- // the continuation data is 0-indexed but the frames are 1-indexed
- ImGui::Text("%4d", _state->_continuationData[_state->_selectedScoreCast.channel][_state->_selectedScoreCast.frame].second + 1); ImGui::SameLine();
- ImGui::SetItemTooltip("End Frame");
- }
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Colors", ImVec2(150.0f, 50.0f));
- ImGui::PopStyleColor();
- ImGui::EndChild();
+ if (castMember || shape) {
+ ImVec4 fg = convertColor(sprite->_foreColor);
+ ImGui::ColorButton("foreColor", fg);
ImGui::SameLine();
-
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Flags", ImVec2(200.0f, 20.0f));
-
- if (castMember || shape) {
- ImGui::Checkbox(ICON_MS_LOCK, &sprite->_enabled); ImGui::SameLine();
- ImGui::SetItemTooltip("enabled");
- ImGui::Checkbox(ICON_MS_EDIT_NOTE, &sprite->_editable); ImGui::SameLine();
- ImGui::SetItemTooltip("editable");
- ImGui::Checkbox(ICON_MS_MOVE_SELECTION_RIGHT, &sprite->_moveable); ImGui::SameLine();
- ImGui::SetItemTooltip("moveable");
- ImGui::Checkbox(ICON_MS_DYNAMIC_FEED, &sprite->_trails);
- ImGui::SetItemTooltip("trails");
- }
- ImGui::PopStyleColor();
- ImGui::EndChild();
-
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Colors", ImVec2(150.0f, 50.0f));
-
- if (castMember || shape) {
- ImVec4 fg = convertColor(sprite->_foreColor);
-
- ImGui::ColorButton("foreColor", fg);
- ImGui::SameLine();
- ImGui::Text("#%02x%02x%02x", (int)(fg.x * 255), (int)(fg.y * 255), (int)(fg.z * 255));
- ImGui::SetItemTooltip("Foreground Color");
- ImVec4 bg = convertColor(sprite->_backColor);
- ImGui::ColorButton("backColor", bg);
- ImGui::SameLine();
- ImGui::Text("#%02x%02x%02x", (int)(bg.x * 255), (int)(bg.y * 255), (int)(bg.z * 255));
- ImGui::SameLine();
- ImGui::SetItemTooltip("Background Color");
- }
-
- ImGui::PopStyleColor();
- ImGui::EndChild();
-
+ ImGui::Text("#%02x%02x%02x", (int)(fg.x * 255), (int)(fg.y * 255), (int)(fg.z * 255));
+ ImGui::SetItemTooltip("Foreground Color");
+ ImVec4 bg = convertColor(sprite->_backColor);
+ ImGui::ColorButton("backColor", bg);
ImGui::SameLine();
-
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Coordinates", ImVec2(150.0f, 50.0f));
-
- if (castMember || shape) {
- ImGui::Text("X:"); ImGui::SameLine();
- ImGui::Text("%d", sprite->_startPoint.x); ImGui::SameLine(75);
- ImGui::SetItemTooltip("Reg Point Horizontal");
- ImGui::Text("W:"); ImGui::SameLine();
- ImGui::Text("%d", sprite->getWidth());
- ImGui::SetItemTooltip("Width");
-
- ImGui::Text("Y:"); ImGui::SameLine();
- ImGui::Text("%d", sprite->_startPoint.y); ImGui::SameLine(75);
- ImGui::SetItemTooltip("Reg Point Vertical");
- ImGui::Text("H:"); ImGui::SameLine();
- ImGui::Text("%d", sprite->getHeight()); ImGui::SameLine();
- ImGui::SetItemTooltip("Height");
- }
- ImGui::PopStyleColor();
- ImGui::EndChild();
-
+ ImGui::Text("#%02x%02x%02x", (int)(bg.x * 255), (int)(bg.y * 255), (int)(bg.z * 255));
ImGui::SameLine();
+ ImGui::SetItemTooltip("Background Color");
+ }
- ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
- ImGui::BeginChild("Bbox", ImVec2(150.0f, 50.0f));
-
- if (castMember || shape) {
- const Common::Rect &box = sprite->getBbox(true);
-
- ImGui::Text("l:"); ImGui::SameLine();
- ImGui::Text("%d", box.left); ImGui::SameLine(75);
- ImGui::SetItemTooltip("Left");
- ImGui::Text("r:"); ImGui::SameLine();
- ImGui::Text("%d", box.right);
- ImGui::SetItemTooltip("Right");
-
- ImGui::Text("t:"); ImGui::SameLine();
- ImGui::Text("%d", box.top); ImGui::SameLine(75);
- ImGui::SetItemTooltip("Top");
- ImGui::Text("b:"); ImGui::SameLine();
- ImGui::Text("%d", box.bottom);
- ImGui::SetItemTooltip("Bottom");
- }
- ImGui::PopStyleColor();
- ImGui::EndChild();
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- ImGui::EndChild();
+ ImGui::SameLine();
+
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Coordinates", ImVec2(150.0f, 50.0f));
+
+ if (castMember || shape) {
+ ImGui::Text("X:"); ImGui::SameLine();
+ ImGui::Text("%d", sprite->_startPoint.x); ImGui::SameLine(75);
+ ImGui::SetItemTooltip("Reg Point Horizontal");
+ ImGui::Text("W:"); ImGui::SameLine();
+ ImGui::Text("%d", sprite->getWidth());
+ ImGui::SetItemTooltip("Width");
+
+ ImGui::Text("Y:"); ImGui::SameLine();
+ ImGui::Text("%d", sprite->_startPoint.y); ImGui::SameLine(75);
+ ImGui::SetItemTooltip("Reg Point Vertical");
+ ImGui::Text("H:"); ImGui::SameLine();
+ ImGui::Text("%d", sprite->getHeight()); ImGui::SameLine();
+ ImGui::SetItemTooltip("Height");
}
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- uint numChannels = MIN<int>(score->_scoreCache[0]->_sprites.size(), score->_maxChannelsUsed + 10);
- uint tableColumns = MAX(numFrames + 5, 25U); // Set minimal table width to 25
+ ImGui::SameLine();
- if (tableColumns > kMaxColumnsInTable - 3) // Current restriction of ImGui
- tableColumns = kMaxColumnsInTable - 3;
+ ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg));
+ ImGui::BeginChild("Bbox", ImVec2(150.0f, 50.0f));
- ImGuiTableFlags addonFlags = _state->_scoreMode == kModeExtended ? 0 : ImGuiTableFlags_RowBg;
+ if (castMember || shape) {
+ const Common::Rect &box = sprite->getBbox(true);
- ImGui::BeginChild("Score table", ImVec2(0, -20));
+ ImGui::Text("l:"); ImGui::SameLine();
+ ImGui::Text("%d", box.left); ImGui::SameLine(75);
+ ImGui::SetItemTooltip("Left");
+ ImGui::Text("r:"); ImGui::SameLine();
+ ImGui::Text("%d", box.right);
+ ImGui::SetItemTooltip("Right");
- if (ImGui::BeginTable("Score", tableColumns + 2,
- ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY |
- addonFlags)) {
- ImGuiTableFlags flags = ImGuiTableColumnFlags_WidthFixed;
+ ImGui::Text("t:"); ImGui::SameLine();
+ ImGui::Text("%d", box.top); ImGui::SameLine(75);
+ ImGui::SetItemTooltip("Top");
+ ImGui::Text("b:"); ImGui::SameLine();
+ ImGui::Text("%d", box.bottom);
+ ImGui::SetItemTooltip("Bottom");
+ }
+ ImGui::PopStyleColor();
+ ImGui::EndChild();
- ImGui::TableSetupScrollFreeze(2, 2);
+ ImGui::EndChild();
+ }
- ImGui::PushFont(_state->_tinyFont);
+}
- ImGui::TableSetupColumn("##disable", flags); // disable button
+static void drawSpriteGrid(ImDrawList *dl, ImVec2 startPos, Score *score, Cast *cast, Window *window) {
+ int total_frames = (int)score->_scoreCache.size();
+
+ auto &cfg = _state->_scoreCfg;
+ float cellH = (_state->_scoreMode == kModeExtended) ? cfg._cellHeightExtended : cfg._cellHeight;
+ int visibleChannels = (_state->_scoreMode == kModeExtended) ? (int)(cfg._tableHeight / cfg._cellHeightExtended) : cfg._visibleChannels;
+ int startFrame = MAX(0, _state->_scoreState.xSliderValue - 1);
+ int numChannels = MIN<int>(score->_scoreCache[0]->_sprites.size(), score->_maxChannelsUsed + 10);
+
+ ImVec2 gridMin = startPos;
+ ImVec2 gridMax = ImVec2(startPos.x + cfg._tableWidth, startPos.y + cfg._tableHeight);
+ dl->PushClipRect(gridMin, gridMax, false);
+
+ for (int i = 0; i < visibleChannels; i++) {
+ int ch = i + _state->_scoreState.channelScrollOffset;
+ if (ch >= numChannels) break;
+ float y = startPos.y + i * cellH;
+
+ // pass 1: draw cell backgrounds
+ for (int f = 0; f < cfg._visibleFrames; f++) {
+ int rf = startFrame + f;
+ float x = startPos.x + f * cfg._cellWidth;
+ ImVec2 cellMin = ImVec2(x, y);
+ ImVec2 cellMax = ImVec2(x + cfg._cellWidth, y + cellH);
+ ImU32 col = ((rf + 1) % 5 == 0) ? cfg._tableDarkColor : cfg._tableLightColor;
+ dl->AddRectFilled(cellMin, cellMax, col);
+ addThinRect(dl, cellMin, cellMax, cfg._borderColor);
+ }
- ImGui::TableSetupColumn("##", flags); // Number
- for (uint i = 0; i < tableColumns; i++) {
- Common::String label = Common::String::format("%-2d", i + _state->_scoreFrameOffset);
- label += Common::String::format("##l%d", i);
+ // pass 2: draw sprite span bars on top of cells
+ for (int f = 0; f < cfg._visibleFrames; f++) {
+ int rf = startFrame + f;
+ if (rf >= total_frames) break;
+ Frame &frame = *score->_scoreCache[rf];
+ Sprite &sprite = *frame._sprites[ch];
- ImGui::TableSetupColumn(label.c_str(), flags);
+ if (!sprite._castId.member && !sprite.isQDShape()) continue; // empty cell
+ if (ch >= (int)_state->_continuationData.size()) continue;
+ if (rf >= (int)_state->_continuationData[ch].size()) break;
+
+ int spanStart = _state->_continuationData[ch][rf].first;
+ int spanEnd = _state->_continuationData[ch][rf].second;
+
+ if (rf != spanStart && f != 0) continue;
+ float x1 = startPos.x + MAX<float>(spanStart - startFrame, 0) * cfg._cellWidth;
+ float x2 = MIN<float>(startPos.x + (spanEnd - startFrame + 1) * cfg._cellWidth, startPos.x + cfg._tableWidth);
+
+ if (ch >= (int)_state->_continuationData.size()) continue;
+ if (rf >= (int)_state->_continuationData[ch].size()) break;
+
+ bool startVisible = spanStart >= startFrame;
+ bool endVisible = spanEnd < startFrame + cfg._visibleFrames;
+
+ // clamp x1 to grid left edge, x2 to right
+ float cy = y + cellH * ( (_state->_scoreMode == kModeExtended) ? 0.1 : 0.2);
+ float pad = 0.0f; // vertical padding so bar doesnt touch cell borders
+
+ // color selection based on if the sprite is selected or not
+ ImU32 color;
+ bool isSelected = (_state->_selectedScoreCast.channel == ch &&
+ _state->_selectedScoreCast.frame >= spanStart &&
+ _state->_selectedScoreCast.frame <= spanEnd);
+ bool isHovered = (_state->_hoveredScoreCast.channel == ch &&
+ _state->_hoveredScoreCast.frame >= spanStart &&
+ _state->_hoveredScoreCast.frame <= spanEnd);
+
+ if (isSelected)
+ color = U32(_state->_colors._channelSelectedCol);
+ else if (isHovered)
+ color = U32(_state->_colors._channelHoveredCol);
+ else {
+ int colorIdx = sprite._colorcode & 0x07;
+ if (colorIdx > 5) colorIdx = 0;
+ color = U32(_state->_colors._contColors[colorIdx]);
}
- ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
- ImGui::TableNextRow(0);
-
- ImGui::TableSetColumnIndex(0);
- ImGui::SetNextItemWidth(20);
-
- ImGui::TableSetColumnIndex(1);
- ImGui::PushID(0);
+ float rounding = 0.0f;
+ dl->AddRectFilled(ImVec2(x1, y + pad), ImVec2(x2 - 1.0f, y + cellH - pad), color, rounding);
+ // horizontal line through the middle, offset from x1 if circle is present
+ dl->AddLine(ImVec2(x1 + (startVisible ? 6.0f : 0.0f), cy), ImVec2(x2 - 6.0f, cy), U32(_state->_colors._line_color), 1.0f);
+
+ if (startVisible)
+ dl->AddCircle(ImVec2(x1 + 4.0f, cy), 3.0f, U32(_state->_colors._line_color), 0, 1.5f);
+
+ if (endVisible)
+ dl->AddRect(ImVec2(x2 - 7.0f, cy - 3.0f), ImVec2(x2 - 1.0f, cy + 3.0f), U32(_state->_colors._line_color), 0.0f, 0, 1.5f);
+
+
+ if (_state->_scoreMode == kModeExtended && startVisible && (sprite._castId.member || sprite.isQDShape())) {
+ float lineH = ImGui::GetTextLineHeight();
+ float textX = x1 + 4.0f;
+ float baseY = y + 15.0f;
+ CastMember *cm = cast->getCastMember(sprite._castId.member, true);
+ // Member name
+ char buf[64] = "";
+ if (cm)
+ snprintf(buf, sizeof(buf), "%s", getDisplayName(cm).c_str());
+ else if (sprite.isQDShape())
+ snprintf(buf, sizeof(buf), "Q");
+ dl->AddText(ImVec2(textX, baseY), U32(_state->_colors._line_color), buf);
+
+ // Behavior
+ buf[0] = '\0';
+ if (sprite._scriptId.member) {
+ CastMember *sc = cast->getCastMember(sprite._scriptId.member, true);
+ if (sc) snprintf(buf, sizeof(buf), "%s", getDisplayName(sc).c_str());
+ }
+ dl->AddText(ImVec2(textX, baseY + lineH), U32(_state->_colors._line_color), buf);
- ImGui::SetNextItemWidth(50);
+ // Ink
+ dl->AddText(ImVec2(textX, baseY + lineH * 2), U32(_state->_colors._line_color), inkType2str(sprite._ink));
- const char *selMode = modes[_state->_scoreMode];
+ // Blend
+ snprintf(buf, sizeof(buf), "%d", sprite._blendAmount);
+ dl->AddText(ImVec2(textX, baseY + lineH * 3), U32(_state->_colors._line_color), buf);
- if (ImGui::BeginCombo("##mode", selMode)) {
- for (int n = 0; n < ARRAYSIZE(modes); n++) {
- const bool selected = (_state->_scoreMode == n);
- if (ImGui::Selectable(Common::String::format("%s##%d", modes[n], n).c_str(), selected))
- _state->_scoreMode = n;
+ // Location
+ snprintf(buf, sizeof(buf), "%d,%d", sprite._startPoint.x, sprite._startPoint.y);
+ dl->AddText(ImVec2(textX, baseY + lineH *4), U32(_state->_colors._line_color), buf);
+ }
- if (selected)
- ImGui::SetItemDefaultFocus();
+ if (startVisible && _state->_scoreMode != kModeExtended) {
+ char label[64] = "";
+ CastMember *cm = cast->getCastMember(sprite._castId.member, true);
+ switch (_state->_scoreMode) {
+ case kModeMember:
+ if (cm)
+ snprintf(label, sizeof(label), "%s", getDisplayName(cm).c_str());
+ else if (sprite.isQDShape())
+ snprintf(label, sizeof(label), "Q");
+ break;
+ case kModeBehavior:
+ if (sprite._scriptId.member) {
+ CastMember *script = cast->getCastMember(sprite._scriptId.member, true);
+ if (script)
+ snprintf(label, sizeof(label), "%s", getDisplayName(script).c_str());
+ }
+ break;
+ case kModeInk:
+ snprintf(label, sizeof(label), "%s", inkType2str(sprite._ink));
+ break;
+ case kModeLocation:
+ snprintf(label, sizeof(label), "%d,%d", sprite._startPoint.x, sprite._startPoint.y);
+ break;
+ case kModeBlend:
+ snprintf(label, sizeof(label), "%d", sprite._blendAmount);
+ break;
+ default:
+ break;
}
- ImGui::EndCombo();
+ if (label[0])
+ dl->AddText(ImVec2(x1 + 4.0f, y + (cellH - ImGui::GetTextLineHeight()) / 2.0f), U32(_state->_colors._line_color), label);
- ImGui::TableHeader("##");
}
- ImGui::PopID();
- for (uint i = 0; i < tableColumns; i++) {
- ImGui::TableSetColumnIndex(i + 2);
- const char *column_name = ImGui::TableGetColumnName(i + 2);
+ }
- ImGui::SetNextItemWidth(20);
- ImGui::TableHeader(column_name);
+ // pass 3, for clickable rects, add invisible buttosn
+ for (int f = 0; f < cfg._visibleFrames; f++) {
+ int rf = startFrame + f;
+ if (rf >= total_frames) break;
+ float x = startPos.x + f * cfg._cellWidth;
+ ImGui::SetCursorScreenPos(ImVec2(x, y));
+ ImGui::InvisibleButton(Common::String::format("##cell_%d_%d", ch, f).c_str(), ImVec2(cfg._cellWidth, cellH));
+ if (ImGui::IsItemClicked()) {
+ _state->_selectedScoreCast.frame = rf;
+ _state->_selectedScoreCast.channel = ch;
+ _state->_selectedScoreCast.isMainChannel = false;
}
- ImGui::TableNextRow();
-
- ImGui::TableNextColumn(); // Enable/Disable switch
-
- ImGui::TableNextColumn(); // Label column
-
- float indentSize = 10.0;
- ImGui::Indent(indentSize);
- ImGui::Text("Labels");
- ImGui::Unindent(indentSize);
+ if (ImGui::IsItemHovered()) {
+ _state->_hoveredScoreCast.frame = rf;
+ _state->_hoveredScoreCast.channel = ch;
+ }
+ }
+ }
+ dl->PopClipRect();
+}
- ImGui::PopFont();
+static void drawSliderX(ImVec2 pos, Score *score) {
+ auto &cfg = _state->_scoreCfg;
+ ImGui::SetCursorScreenPos(pos);
+ ImGui::SetNextItemWidth(cfg._rulerWidth);
+ int totalFrames = (int)score->_scoreCache.size();
+ int sliderMin = 1;
+ int sliderMax = MAX(totalFrames - cfg._visibleFrames + 1, 1);
+ ImGui::SliderInt("##frameSlider", &_state->_scoreState.xSliderValue, sliderMin, sliderMax);
+}
- if (score->_labels && score->_labels->size()) {
- auto labels = *score->_labels;
- auto it = labels.begin();
+static void drawMainChannelGrid(ImDrawList *dl, ImVec2 startPos, Score *score) {
+ auto &cfg = _state->_scoreCfg;
+ int startFrame = MAX(0, _state->_scoreState.xSliderValue - 1);
+ int total_frames = (int)score->_scoreCache.size();
+
+ for (int ch = kChTempo; ch <= kChScript; ch++) {
+ float y = startPos.y + ch * cfg._cellHeight;
+
+ // pass 1, backgrounds
+ for (int f = 0; f <= cfg._visibleFrames; f++) {
+ int rf = startFrame + f;
+ float x = startPos.x + f * cfg._cellWidth;
+ ImVec2 cellMin = ImVec2(x, y);
+ ImVec2 cellMax = ImVec2(x + cfg._cellWidth, y + cfg._cellHeight);
+ ImU32 col = ((rf + 1) % 5 == 0) ? cfg._tableDarkColor : cfg._tableLightColor;
+ dl->AddRectFilled(cellMin, cellMax, col);
+ addThinRect(dl, cellMin, cellMax, cfg._borderColor);
+ }
- for (uint f = 0; f < tableColumns; f++) {
- ImGui::TableNextColumn();
+ // pass 2, span bars
+ int f = 0;
+ while (f < cfg._visibleFrames) {
+ int rf = startFrame + f;
+ if (rf >= total_frames) break;
+
+ // get label for this frame
+ Frame &frame = *score->_scoreCache[rf];
+ auto &mc = frame._mainChannels;
+ Common::String label;
+ switch (ch) {
+ case kChTempo: if (mc.tempo) label = Common::String::format("%d", mc.tempo); break;
+ case kChPalette: if (mc.palette.paletteId.member) label = Common::String::format("%d", mc.palette.paletteId.member); break;
+ case kChTransition: if (mc.transType) label = Common::String::format("%d", mc.transType); break;
+ case kChSound1: if (mc.sound1.member) label = Common::String::format("%d", mc.sound1.member); break;
+ case kChSound2: if (mc.sound2.member) label = Common::String::format("%d", mc.sound2.member); break;
+ case kChScript: if (mc.actionId.member) label = Common::String::format("%d", mc.actionId.member); break;
+ }
- while (it != labels.end() && (*it)->number < f + _state->_scoreFrameOffset)
- it++;
+ if (label.empty()) { f++; continue; }
+
+ // find run end (same value)
+ int runStart = rf, runEnd = rf;
+ for (int nf = rf + 1; nf < total_frames && (nf - startFrame) < cfg._visibleFrames; nf++) {
+ Common::String nextLabel;
+ Frame &nframe = *score->_scoreCache[nf];
+ auto &nmc = nframe._mainChannels;
+ switch (ch) {
+ case kChTempo: if (nmc.tempo) nextLabel = Common::String::format("%d", nmc.tempo); break;
+ case kChPalette: if (nmc.palette.paletteId.member) nextLabel = Common::String::format("%d", nmc.palette.paletteId.member); break;
+ case kChTransition: if (nmc.transType) nextLabel = Common::String::format("%d", nmc.transType); break;
+ case kChSound1: if (nmc.sound1.member) nextLabel = Common::String::format("%d", nmc.sound1.member); break;
+ case kChSound2: if (nmc.sound2.member) nextLabel = Common::String::format("%d", nmc.sound2.member); break;
+ case kChScript: if (nmc.actionId.member) nextLabel = Common::String::format("%d", nmc.actionId.member); break;
+ }
+ if (label == nextLabel) runEnd = nf;
+ else break;
+ }
- if (it == labels.end())
- continue;
+ bool startVisible = (runStart >= startFrame);
+ bool endVisible = (runEnd < startFrame + cfg._visibleFrames);
+ float x1 = startPos.x + MAX<float>(runStart - startFrame, 0) * cfg._cellWidth;
+ float x2 = MIN<float>(startPos.x + (runEnd - startFrame + 1) * cfg._cellWidth, startPos.x + cfg._tableWidth);
+ float cy = y + cfg._cellHeight * 0.2;
+ float pad = 0.0f;
+
+ dl->AddRectFilled(ImVec2(x1, y + pad), ImVec2(x2 - 1.0f, y + cfg._cellHeight - pad), U32(_state->_colors._contColors[ch % 6]), 0.0f);
+ dl->AddLine(ImVec2(x1 + (startVisible ? 6.0f : 0.0f), cy), ImVec2(x2 - 6.0f, cy), U32(_state->_colors._line_color), 1.0f);
+ if (startVisible)
+ dl->AddCircle(ImVec2(x1 + 4.0f, cy), 3.0f, U32(_state->_colors._line_color), 0, 1.5f);
+ if (endVisible)
+ dl->AddRect(ImVec2(x2 - 7.0f, cy - 3.0f), ImVec2(x2 - 1.0f, cy + 3.0f), U32(_state->_colors._line_color), 0.0f, 0, 1.5f);
+ f = (runEnd - startFrame) + 1; // skip to after the run
+ }
- if ((*it)->number == f + _state->_scoreFrameOffset) {
- ImGui::Text(ICON_MS_BEENHERE);
- ImGui::SetItemTooltip((*it)->name.c_str());
+ // pass 3: invisible buttons for interaction
+ for (f = 0; f < cfg._visibleFrames; f++) {
+ int rf = startFrame + f;
+ if (rf >= total_frames) break;
+ float x = startPos.x + f * cfg._cellWidth;
+ ImGui::SetCursorScreenPos(ImVec2(x, y));
+ ImGui::InvisibleButton(
+ Common::String::format("##maincell_%d_%d", ch, f).c_str(),
+ ImVec2(cfg._cellWidth, cfg._cellHeight)
+ );
+
+ if (ImGui::IsItemClicked()) {
+ _state->_selectedScoreCast.frame = rf;
+ _state->_selectedScoreCast.channel = ch;
+ _state->_selectedScoreCast.isMainChannel = true;
+ Frame &clickedFrame = *score->_scoreCache[rf];
+ auto &mc = clickedFrame._mainChannels;
+ switch (ch) {
+ case kChTempo:
+ break;
+ case kChPalette: // open cast window focused on palette member
+ if (mc.palette.paletteId.member) {
+ _state->_w.cast = true;
+ // select it in the cast so showCast() highlights it
+ for (auto &scriptCast : _state->_scriptCasts) {
+ if (scriptCast == mc.palette.paletteId) {
+ break;
+ }
+ }
+ }
+ break;
+ case kChTransition:
+ break;
+ case kChSound1: // open cast window
+ if (mc.sound1.member)
+ _state->_w.cast = true;
+ break;
+ case kChSound2:
+ if (mc.sound2.member)
+ _state->_w.cast = true;
+ break;
+ case kChScript: // open script in script editor
+ if (mc.actionId.member) {
+ ImGuiScript script = toImGuiScript(kScoreScript, mc.actionId, "");
+ setScriptToDisplay(script);
+ _state->_w.funcList = true;
}
+ break;
}
}
- {
- displayScoreChannel(0, kChTempo, 0, selectedWindow);
- displayScoreChannel(0, kChPalette, 0, selectedWindow);
- displayScoreChannel(0, kChTransition, 0, selectedWindow);
- displayScoreChannel(0, kChSound1, 0, selectedWindow);
- displayScoreChannel(0, kChSound2, 0, selectedWindow);
- displayScoreChannel(0, kChScript, 0, selectedWindow);
+ if (ImGui::IsItemHovered()) {
+ Common::String label;
+ Frame &frame = *score->_scoreCache[rf];
+ auto &mc = frame._mainChannels;
+ switch (ch) {
+ case kChTempo: if (mc.tempo) label = Common::String::format("%d", mc.tempo); break;
+ case kChPalette: if (mc.palette.paletteId.member) label = Common::String::format("%d", mc.palette.paletteId.member); break;
+ case kChTransition: if (mc.transType) label = Common::String::format("%d", mc.transType); break;
+ case kChSound1: if (mc.sound1.member) label = Common::String::format("%d", mc.sound1.member); break;
+ case kChSound2: if (mc.sound2.member) label = Common::String::format("%d", mc.sound2.member); break;
+ case kChScript: if (mc.actionId.member) label = Common::String::format("%d", mc.actionId.member); break;
+ }
+ if (!label.empty())
+ ImGui::SetTooltip("%s: %s", modes2[(ch) * 2 + 1], label.c_str());
}
- ImGui::TableNextRow();
+ }
+ }
+}
- int mode = _state->_scoreMode;
+static void drawPlayhead(ImDrawList *dl, ImVec2 rulerPos, ImVec2 mainChannelGridPos, ImVec2 gridPos, Score *score) {
+ auto &cfg = _state->_scoreCfg;
+ int start = _state->_scoreState.xSliderValue;
+ uint currentFrameNum = score->getCurrentFrameNum();
- for (int ch = 0; ch < (int)numChannels - 1; ch++) {
- if (mode == kModeExtended) // This will render empty row
- displayScoreChannel(ch + 1, kModeExtended, _state->_scoreMode, selectedWindow);
+ if ((int)currentFrameNum < start || (int)currentFrameNum >= start + cfg._visibleFrames)
+ return;
- if (mode == kModeMember || mode == kModeExtended)
- displayScoreChannel(ch + 1, kModeMember, _state->_scoreMode, selectedWindow);
+ float px = rulerPos.x + (currentFrameNum - start) * cfg._cellWidth;
+ float top = mainChannelGridPos.y; // top of main channel grid
+ float bottom = gridPos.y + cfg._tableHeight; // bottom of sprite grid
+ ImU32 RED = IM_COL32(200, 50, 0, 255);
- if (mode == kModeBehavior || mode == kModeExtended)
- displayScoreChannel(ch + 1, kModeBehavior, _state->_scoreMode, selectedWindow);
+ dl->AddLine(ImVec2(px, top), ImVec2(px, bottom), RED, 2.0f);
- if (mode == kModeInk || mode == kModeExtended)
- displayScoreChannel(ch + 1, kModeInk, _state->_scoreMode, selectedWindow);
+ // triangle marker in the ruler
+ dl->AddTriangleFilled(
+ ImVec2(px - 5.0f, rulerPos.y),
+ ImVec2(px + 5.0f, rulerPos.y),
+ ImVec2(px, rulerPos.y + 8.0f),
+ RED
+ );
+}
- if (mode == kModeBlend || mode == kModeExtended)
- displayScoreChannel(ch + 1, kModeBlend, _state->_scoreMode, selectedWindow);
+void showScore() {
+ if (!_state->_w.score)
+ return;
- if (mode == kModeLocation || mode == kModeExtended)
- displayScoreChannel(ch + 1, kModeLocation, _state->_scoreMode, selectedWindow);
- }
- ImGui::EndTable();
- }
+ ImVec2 pos(20, 20);
+ ImGui::SetNextWindowPos(pos, ImGuiCond_FirstUseEver);
- ImGui::EndChild();
+ ImVec2 windowSize = ImGui::GetMainViewport()->Size * 1.5f;
+ ImGui::SetNextWindowSize(windowSize, ImGuiCond_FirstUseEver);
- { // Render pagination
- ImGui::BeginDisabled(numFrames <= FRAME_PAGE_SIZE);
- ImGui::Text(" Jump to frame:");
- ImGui::SameLine();
- ImGui::SliderInt("##scorepage", &_state->_scorePageSlider, 0, numFrames / FRAME_PAGE_SIZE, "%d00");
- _state->_scoreFrameOffset = _state->_scorePageSlider * FRAME_PAGE_SIZE + 1;
- ImGui::EndDisabled();
+ if (ImGui::Begin("Score", &_state->_w.score, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
+ Window *selectedWindow = windowListCombo(&_state->_scoreWindow);
- ImGui::SameLine();
- ImGui::Button(ICON_MS_ALIGN_JUSTIFY_CENTER, ImVec2(20, 20));
- ImGui::SetItemTooltip("Center View");
+ buildContinuationData(selectedWindow);
+
+ Score *score = selectedWindow->getCurrentMovie()->getScore();
+ uint numFrames = score->_scoreCache.size();
+ Cast *cast = selectedWindow->getCurrentMovie()->getCast();
+
+ if (!numFrames) {
+ ImGui::Text("No frames");
+ ImGui::End();
+ return;
}
+ if (_state->_selectedScoreCast.frame >= (int)numFrames)
+ _state->_selectedScoreCast.frame = 0;
+
+ if (!numFrames || _state->_selectedScoreCast.channel >= (int)score->_scoreCache[0]->_sprites.size())
+ _state->_selectedScoreCast.channel = 0;
+
+ drawSpriteInspector(score, cast, numFrames);
+ ImDrawList *dl = ImGui::GetWindowDrawList();
+ ImVec2 origin = ImGui::GetCursorScreenPos();
+ ScoreLayout layout = computeLayout(origin, _state->_scoreCfg);
+ int numChannels = MIN<int>(score->_scoreCache[0]->_sprites.size(), score->_maxChannelsUsed + 10);
+
+ drawSidebar1(dl, layout.sidebar1Pos, score);
+ drawMainChannelGrid(dl, layout.mainChannelGridPos, score);
+ drawModeSelector(layout.modeSelectorPos);
+ drawRuler(dl, layout.rulerPos);
+ drawSidebar2(dl, layout.sidebar2Pos, score);
+ drawSpriteGrid(dl, layout.gridPos, score, cast, selectedWindow);
+ drawPlayhead(dl, layout.rulerPos, layout.mainChannelGridPos, layout.gridPos, score);
+ drawSliderX(layout.sliderPos, score);
+ drawSliderY(layout.sliderYPos, numChannels);
+
}
ImGui::End();
}
@@ -742,12 +937,12 @@ void showChannels() {
ImGui::Text("TMPO: tempo: %d, skipFrameFlag: %d, blend: %d, currentFPS: %d",
frame._mainChannels.tempo, frame._mainChannels.skipFrameFlag, frame._mainChannels.blend, score->_currentFrameRate);
if (!frame._mainChannels.palette.paletteId.isNull()) {
- ImGui::Text("PAL: paletteId: %s, firstColor: %d, lastColor: %d, flags: %d, cycleCount: %d, speed: %d, frameCount: %d, fade: %d, delay: %d, style: %d, currentId: %s, defaultId: %s",
+ ImGui::Text("PAL: paletteId: %s, firstColor: %d, lastColor: %d, flags: %d, cycleCount: %d, speed: %d, frameCount: %d, fade: %d, delay: %d, style: %d, currentId: %s, defaultId: %s",
frame._mainChannels.palette.paletteId.asString().c_str(), frame._mainChannels.palette.firstColor, frame._mainChannels.palette.lastColor, frame._mainChannels.palette.flags,
frame._mainChannels.palette.cycleCount, frame._mainChannels.palette.speed, frame._mainChannels.palette.frameCount,
frame._mainChannels.palette.fade, frame._mainChannels.palette.delay, frame._mainChannels.palette.style, g_director->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
} else {
- ImGui::Text("PAL: paletteId: 000, currentId: %s, defaultId: %s\n", g_director->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
+ ImGui::Text("PAL: paletteId: 000, currentId: %s, defaultId: %s\n", g_director->_lastPalette.asString().c_str(), defaultPalette.asString().c_str());
}
ImGui::Text("TRAN: transType: %d, transDuration: %d, transChunkSize: %d",
frame._mainChannels.transType, frame._mainChannels.transDuration, frame._mainChannels.transChunkSize);
@@ -818,9 +1013,9 @@ void showChannels() {
}
if (score->_channels[i]->_visible)
- dl->AddCircleFilled(mid, 4.0f, ImColor(_state->_colors._channel_toggle));
+ dl->AddCircleFilled(mid, 4.0f, U32(_state->_colors._channel_toggle));
else
- dl->AddCircle(mid, 4.0f, ImColor(_state->_colors._channel_toggle));
+ dl->AddCircle(mid, 4.0f, U32(_state->_colors._channel_toggle));
ImGui::PopID();
}
More information about the Scummvm-git-logs
mailing list