Files
scummvm-cursorfix/engines/director/debugger/dt-cast.cpp
2026-02-02 04:50:13 +01:00

340 lines
10 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "backends/imgui/IconsMaterialSymbols.h"
#include "backends/imgui/imgui_utils.h"
#include "director/director.h"
#include "director/debugger/dt-internal.h"
#include "director/cast.h"
#include "director/castmember/bitmap.h"
#include "director/castmember/text.h"
#include "director/castmember/shape.h"
#include "director/castmember/richtext.h"
#include "director/castmember/script.h"
#include "director/movie.h"
#include "director/types.h"
#include "director/window.h"
namespace Director {
namespace DT {
static const char *toString(ScriptType scriptType) {
static const char *scriptTypes[] = {
"Score",
"Cast",
"Movie",
"Event",
"Test",
"???",
"???",
"Parent",
};
if (scriptType < 0 || scriptType > kMaxScriptType)
return "???";
return scriptTypes[(int)scriptType];
}
static const char *toIcon(CastType castType) {
static const char *castTypes[] = {
"", // Empty
ICON_MS_BACKGROUND_DOT_LARGE, // Bitmap
ICON_MS_THEATERS, // FilmLoop
ICON_MS_MATCH_CASE, // Text
ICON_MS_PALETTE, // Palette
ICON_MS_IMAGESMODE, // Picture
ICON_MS_VOLUME_UP, // Sound
ICON_MS_SLAB_SERIF, // Button
ICON_MS_SHAPES, // Shape
ICON_MS_MOVIE, // Movie
ICON_MS_ANIMATED_IMAGES, // DigitalVideo
ICON_MS_FORMS_APPS_SCRIPT, // Script
ICON_MS_BRAND_FAMILY, // RTE
"?", // ???
ICON_MS_TRANSITION_FADE}; // Transition
if (castType < 0 || castType > kCastTransition)
return "";
return castTypes[(int)castType];
}
const char *toString(CastType castType) {
static const char *castTypes[] = {
"Empty",
"Bitmap",
"FilmLoop",
"Text",
"Palette",
"Picture",
"Sound",
"Button",
"Shape",
"Movie",
"DigitalVideo",
"Script",
"RTE",
"???",
"Transition"};
if (castType < 0 || castType > kCastTransition)
return "???";
return castTypes[(int)castType];
}
Common::String getDisplayName(CastMember *castMember) {
const CastMemberInfo *castMemberInfo = castMember->getInfo();
Common::String name(castMemberInfo ? castMemberInfo->name : "");
if (!name.empty())
return name;
if (castMember->_type == kCastText) {
TextCastMember *textCastMember = (TextCastMember *)castMember;
return textCastMember->getText();
}
return Common::String::format("%u", castMember->getID());
}
void showCast() {
if (!_state->_w.cast)
return;
ImGui::SetNextWindowPos(ImVec2(20, 160), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(480, 480), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Cast", &_state->_w.cast)) {
Window *selectedWindow = windowListCombo(&_state->_castWindow);
// display a toolbar with: grid/list/filters buttons + name filter
ImGuiEx::toggleButton(ICON_MS_LIST, &_state->_cast._listView);
ImGui::SetItemTooltip("List");
ImGui::SameLine();
ImGuiEx::toggleButton(ICON_MS_GRID_VIEW, &_state->_cast._listView, true);
ImGui::SetItemTooltip("Grid");
ImGui::SameLine();
if (ImGui::Button(ICON_MS_FILTER_ALT)) {
ImGui::OpenPopup("filters_popup");
}
ImGui::SameLine();
if (ImGui::BeginPopup("filters_popup")) {
ImGui::CheckboxFlags("All", &_state->_cast._typeFilter, 0x7FFF);
ImGui::Separator();
for (int i = 0; i <= 14; i++) {
ImGui::PushID(i);
Common::String option(Common::String::format("%s %s", toIcon((CastType)i), toString((CastType)i)));
ImGui::CheckboxFlags(option.c_str(), &_state->_cast._typeFilter, 1 << i);
ImGui::PopID();
}
ImGui::EndPopup();
}
_state->_cast._nameFilter.Draw();
ImGui::Separator();
// display a list or a grid
const float sliderHeight = _state->_cast._listView ? 0.f : 38.f;
const ImVec2 childsize = ImGui::GetContentRegionAvail();
Movie *movie = selectedWindow->getCurrentMovie();
ImGui::BeginChild("##cast", ImVec2(childsize.x, childsize.y - sliderHeight));
if (_state->_cast._listView) {
if (ImGui::BeginTable("Resources", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn("Name", 0, 120.f);
ImGui::TableSetupColumn("#", 0, 20.f);
ImGui::TableSetupColumn("Script", 0, 80.f);
ImGui::TableSetupColumn("Type", 0, 80.f);
ImGui::TableSetupColumn("Preview", ImGuiTableColumnFlags_WidthStretch, 50.f);
ImGui::TableHeadersRow();
for (auto it : *movie->getCasts()) {
Cast *cast = it._value;
if (!cast->_loadedCast)
continue;
for (auto castMember : *cast->_loadedCast) {
if (!castMember._value->isLoaded())
continue;
Common::String name(getDisplayName(castMember._value));
if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
continue;
if ((castMember._value->_type != kCastTypeAny) && !(_state->_cast._typeFilter & (1 << (int)castMember._value->_type)))
continue;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%s %s", toIcon(castMember._value->_type), name.c_str());
ImGui::TableNextColumn();
ImGui::Text("%d", castMember._key);
ImGui::TableNextColumn();
if (castMember._value->_type == CastType::kCastLingoScript) {
ScriptCastMember *scriptMember = (ScriptCastMember *)castMember._value;
ImGui::Text("%s", toString(scriptMember->_scriptType));
}
ImGui::TableNextColumn();
ImGui::Text("%s", toString(castMember._value->_type));
ImGui::TableNextColumn();
float columnWidth = ImGui::GetColumnWidth();
ImGuiImage imgID = {};
switch (castMember._value->_type) {
case kCastBitmap:
{
imgID = getImageID(castMember._value);
if (imgID.id) {
float offsetX = (columnWidth - 32.f) * 0.5f;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
showImage(imgID, name.c_str(), 32.f);
}
}
break;
case kCastText:
case kCastRichText:
case kCastButton:
{
imgID = getTextID(castMember._value);
if (imgID.id) {
float offsetX = (columnWidth - 32.f) * 0.5f;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
showImage(imgID, name.c_str(), 32.f);
}
}
break;
case kCastShape:
{
imgID = getShapeID(castMember._value);
if (imgID.id) {
float offsetX = (columnWidth - 32.f) * 0.5f;
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offsetX);
showImage(imgID, name.c_str(), 32.f);
}
}
break;
default:
break;
}
}
}
ImGui::EndTable();
}
} else {
const float thumbnailSize = (float)_state->_cast._thumbnailSize;
const float contentWidth = ImGui::GetContentRegionAvail().x;
int columns = contentWidth / (thumbnailSize + 8.f);
columns = columns < 1 ? 1 : columns;
if (ImGui::BeginTable("Cast", columns)) {
for (auto it : *movie->getCasts()) {
const Cast *cast = it._value;
if (!cast->_loadedCast)
continue;
for (auto castMember : *cast->_loadedCast) {
if (!castMember._value->isLoaded())
continue;
Common::String name(getDisplayName(castMember._value));
if (!_state->_cast._nameFilter.PassFilter(name.c_str()))
continue;
if ((castMember._value->_type != kCastTypeAny) && !(_state->_cast._typeFilter & (1 << (int)castMember._value->_type)))
continue;
ImGui::TableNextColumn();
ImGui::BeginGroup();
const ImVec2 textSize = ImGui::CalcTextSize(name.c_str());
float textWidth = textSize.x;
float textHeight = textSize.y;
if (textWidth > thumbnailSize) {
textWidth = thumbnailSize;
textHeight *= (textSize.x / textWidth);
}
ImGuiImage imgID = {};
switch (castMember._value->_type) {
case kCastBitmap:
{
imgID = getImageID(castMember._value);
if (imgID.id) {
showImage(imgID, name.c_str(), thumbnailSize);
}
}
break;
case kCastText:
case kCastRichText:
case kCastButton:
{
imgID = getTextID(castMember._value);
if (imgID.id) {
showImage(imgID, name.c_str(), thumbnailSize);
}
}
break;
case kCastShape:
{
imgID = getShapeID(castMember._value);
if (imgID.id) {
showImage(imgID, name.c_str(), thumbnailSize);
}
}
break;
default:
break;
}
if (!imgID.id) {
ImGui::PushID(castMember._key);
ImGui::InvisibleButton("##canvas", ImVec2(thumbnailSize, thumbnailSize));
ImGui::PopID();
const ImVec2 p0 = ImGui::GetItemRectMin();
const ImVec2 p1 = ImGui::GetItemRectMax();
ImGui::PushClipRect(p0, p1, true);
ImDrawList *draw_list = ImGui::GetWindowDrawList();
draw_list->AddRect(p0, p1, IM_COL32_WHITE);
const ImVec2 pos = p0 + ImVec2((thumbnailSize - textWidth) * 0.5f, (thumbnailSize - textHeight) * 0.5f);
draw_list->AddText(nullptr, 0.f, pos, IM_COL32_WHITE, name.c_str(), 0, thumbnailSize);
draw_list->AddText(nullptr, 0.f, p1 - ImVec2(16, 16), IM_COL32_WHITE, toIcon(castMember._value->_type));
ImGui::PopClipRect();
}
ImGui::EndGroup();
}
}
ImGui::EndTable();
}
}
ImGui::EndChild();
// in the footer display a slider for the grid view: thumbnail size
if (!_state->_cast._listView) {
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
ImGui::SliderInt("Thumbnail Size", &_state->_cast._thumbnailSize, 32, 256);
}
}
ImGui::End();
}
} // namespace DT
} // namespace Director