diff --git a/mod.json b/mod.json index 529364e..761f20f 100644 --- a/mod.json +++ b/mod.json @@ -1,5 +1,5 @@ { - "geode": "4.3.1", + "geode": "4.4.0", "version": "v1.8.0", "gd": { "win": "*", diff --git a/src/DevTools.cpp b/src/DevTools.cpp index 84d0f01..1434ca5 100644 --- a/src/DevTools.cpp +++ b/src/DevTools.cpp @@ -24,7 +24,9 @@ struct matjson::Serialize { .orderChildren = value["order_children"].asBool().unwrapOr(std::move(defaults.orderChildren)), .advancedSettings = value["advanced_settings"].asBool().unwrapOr(std::move(defaults.advancedSettings)), .showMemoryViewer = value["show_memory_viewer"].asBool().unwrapOr(std::move(defaults.showMemoryViewer)), + .showModGraph = value["show_mod_graph"].asBool().unwrapOr(std::move(defaults.showModGraph)), .theme = value["theme"].asString().unwrapOr(std::move(defaults.theme)), + .themeColor = value["theme_color"].as().isOk() ? value["theme_color"].as().unwrap() : std::move(defaults.themeColor) }); } @@ -38,7 +40,9 @@ struct matjson::Serialize { { "order_children", settings.orderChildren }, { "advanced_settings", settings.advancedSettings }, { "show_memory_viewer", settings.showMemoryViewer }, + { "show_mod_graph", settings.showModGraph }, { "theme", settings.theme }, + { "theme_color", settings.themeColor }, }); } }; @@ -52,6 +56,7 @@ DevTools* DevTools::get() { void DevTools::loadSettings() { m_settings = Mod::get()->getSavedValue("settings"); } void DevTools::saveSettings() { Mod::get()->setSavedValue("settings", m_settings); } +Settings DevTools::getSettings() { return m_settings; } bool DevTools::shouldPopGame() const { return m_visible && m_settings.GDInWindow; @@ -126,12 +131,15 @@ void DevTools::drawPages() { &DevTools::drawSettings ); + // if advanced ever has more than one option, add it back +#if 0 if (m_settings.advancedSettings) { this->drawPage( U8STR(FEATHER_SETTINGS " Advanced Settings###devtools/advanced/settings"), &DevTools::drawAdvancedSettings ); } +#endif this->drawPage( U8STR(FEATHER_TOOL " Attributes###devtools/attributes"), @@ -146,7 +154,7 @@ void DevTools::drawPages() { ); #endif - if (m_showModGraph) { + if (m_settings.showModGraph) { this->drawPage( U8STR(FEATHER_SHARE_2 " Mod Graph###devtools/advanced/mod-graph"), &DevTools::drawModGraph @@ -154,7 +162,10 @@ void DevTools::drawPages() { } if (m_settings.showMemoryViewer) { - this->drawPage("Memory viewer", &DevTools::drawMemory); + this->drawPage( + U8STR(FEATHER_TERMINAL " Memory viewer"), + &DevTools::drawMemory + ); } } diff --git a/src/DevTools.hpp b/src/DevTools.hpp index 553c9f3..e9da380 100644 --- a/src/DevTools.hpp +++ b/src/DevTools.hpp @@ -26,7 +26,9 @@ struct Settings { bool orderChildren = true; bool advancedSettings = false; bool showMemoryViewer = false; + bool showModGraph = false; std::string theme = DARK_THEME; + ccColor4B themeColor = {2, 119, 189, 255}; }; class DevTools { @@ -81,7 +83,7 @@ class DevTools { static DevTools* get(); void loadSettings(); void saveSettings(); - + Settings getSettings(); bool shouldUseGDWindow() const; bool shouldPopGame() const; diff --git a/src/pages/Advanced.cpp b/src/pages/Advanced.cpp index 88751e4..b7eb159 100644 --- a/src/pages/Advanced.cpp +++ b/src/pages/Advanced.cpp @@ -7,7 +7,6 @@ using namespace geode::prelude; void DevTools::drawAdvancedSettings() { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 1.f, 1.f }); - ImGui::Checkbox("Show Mod Graph", &m_showModGraph); ImGui::PopStyleVar(); } diff --git a/src/pages/Attributes.cpp b/src/pages/Attributes.cpp index 579878e..90c9a36 100644 --- a/src/pages/Attributes.cpp +++ b/src/pages/Attributes.cpp @@ -37,6 +37,10 @@ void DevTools::drawNodeAttributes(CCNode* node) { if (ImGui::Button("Deselect")) { return this->selectNode(nullptr); } + ImGui::SameLine(); + if (ImGui::Button(U8STR(FEATHER_COPY " Copy Class Name"))) { + clipboard::write(getNodeName(node)); + } ImGui::Text("Address: %s", fmt::to_string(fmt::ptr(node)).c_str()); ImGui::SameLine(); if (ImGui::Button(U8STR(FEATHER_COPY " Copy"))) { @@ -59,22 +63,6 @@ void DevTools::drawNodeAttributes(CCNode* node) { ImGui::Text("Node ID: N/A"); } - if (auto menuItemNode = typeinfo_cast(node)) { - const auto selector = menuItemNode->m_pfnSelector; - if (!selector) { - std::string addr = "N/A"; - ImGui::Text("CCMenuItem selector: %s", addr.c_str()); - } else { - const auto addr = formatAddressIntoOffset(addresser::getNonVirtual(selector), true); - ImGui::Text("CCMenuItem selector: %s", addr.c_str()); - ImGui::SameLine(); - if (ImGui::Button(U8STR(FEATHER_COPY " Copy##copymenuitem"))) { - const auto addrNoModule = formatAddressIntoOffset(addresser::getNonVirtual(selector), false); - clipboard::write(addrNoModule); - } - } - } - float pos[2] = { node->getPositionX(), node->getPositionY() @@ -121,6 +109,26 @@ void DevTools::drawNodeAttributes(CCNode* node) { if (node->getZOrder() != zOrder) { node->setZOrder(zOrder); } + int tag = node->getTag(); + if (ImGui::InputInt("Tag", &tag)) { + node->setTag(tag); + } + + if (auto delegate = typeinfo_cast(node)) { + if (auto handler = CCTouchDispatcher::get()->findHandler(delegate)) { + auto priority = handler->getPriority(); + + if (ImGui::InputInt("Touch Priority", &priority)) { + CCTouchDispatcher::get()->setPriority(priority, handler->getDelegate()); + } + } + } + + if (auto sprite = typeinfo_cast(node)) { + checkbox("Flip X", sprite, &CCSprite::isFlipX, &CCSprite::setFlipX); + ImGui::SameLine(); + checkbox("Flip Y", sprite, &CCSprite::isFlipY, &CCSprite::setFlipY); + } checkbox("Visible", node, &CCNode::isVisible, &CCNode::setVisible); checkbox( @@ -131,36 +139,84 @@ void DevTools::drawNodeAttributes(CCNode* node) { ); if (auto rgbaNode = typeinfo_cast(node)) { - auto color = rgbaNode->getColor(); - float _color[4] = { color.r / 255.f, color.g / 255.f, color.b / 255.f, rgbaNode->getOpacity() / 255.f }; - if (ImGui::ColorEdit4("Color", _color)) { - rgbaNode->setColor(ccColor3B { - static_cast(_color[0] * 255), - static_cast(_color[1] * 255), - static_cast(_color[2] * 255) - }); - - rgbaNode->setOpacity(static_cast(_color[3] * 255)); + + if (auto gradient = typeinfo_cast(node)) { + { + auto color = gradient->getStartColor(); + float _color[4] = { color.r / 255.f, color.g / 255.f, color.b / 255.f, gradient->getStartOpacity() / 255.f }; + if (ImGui::ColorEdit4("Color 1", _color)) { + gradient->setStartColor(ccColor3B{ + static_cast(_color[0] * 255), + static_cast(_color[1] * 255), + static_cast(_color[2] * 255) + }); + + gradient->setStartOpacity(static_cast(_color[3] * 255)); + } + } + { + auto color = gradient->getEndColor(); + float _color[4] = { color.r / 255.f, color.g / 255.f, color.b / 255.f, gradient->getEndOpacity() / 255.f }; + if (ImGui::ColorEdit4("Color 2", _color)) { + gradient->setEndColor(ccColor3B{ + static_cast(_color[0] * 255), + static_cast(_color[1] * 255), + static_cast(_color[2] * 255) + }); + + gradient->setEndOpacity(static_cast(_color[3] * 255)); + } + } + CCPoint gradientVector = gradient->getVector(); + float vector[2] = { + gradientVector.x, + gradientVector.y + }; + if (ImGui::DragFloat2("Vector", vector, 0.05f)) { + gradient->setVector({vector[0], vector[1]}); + } } - } + else { + auto color = rgbaNode->getColor(); + float _color[4] = { color.r / 255.f, color.g / 255.f, color.b / 255.f, rgbaNode->getOpacity() / 255.f }; + if (ImGui::ColorEdit4("Color", _color)) { + rgbaNode->setColor(ccColor3B{ + static_cast(_color[0] * 255), + static_cast(_color[1] * 255), + static_cast(_color[2] * 255) + }); + rgbaNode->setOpacity(static_cast(_color[3] * 255)); + } + } + checkbox("Cascade Color", rgbaNode, &CCRGBAProtocol::isCascadeColorEnabled, &CCRGBAProtocol::setCascadeColorEnabled); + ImGui::SameLine(); + checkbox("Cascade Opacity", rgbaNode, &CCRGBAProtocol::isCascadeOpacityEnabled, &CCRGBAProtocol::setCascadeOpacityEnabled); + } + if (auto labelNode = typeinfo_cast(node)) { std::string str = labelNode->getString(); if (ImGui::InputText("Text", &str, 256)) { labelNode->setString(str.c_str()); } } + + if (auto gap = typeinfo_cast(node)){ + float axisGap = gap->getGap(); + if (ImGui::DragFloat("Axis Gap", &axisGap)) { + gap->setGap(axisGap); + if (CCNode* parent = node->getParent()) { + parent->updateLayout(); + } + } + } + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); if (auto textureProtocol = typeinfo_cast(node)) { if (auto texture = textureProtocol->getTexture()) { - auto* cachedTextures = CCTextureCache::sharedTextureCache()->m_pTextures; - for (auto [key, obj] : CCDictionaryExt(cachedTextures)) { - if (obj == texture) { - ImGui::TextWrapped("Texture name: %s", key.c_str()); - break; - } - } - if (auto spriteNode = typeinfo_cast(node)) { auto* cachedFrames = CCSpriteFrameCache::sharedSpriteFrameCache()->m_pSpriteFrames; const auto rect = spriteNode->getTextureRect(); @@ -174,15 +230,87 @@ void DevTools::drawNodeAttributes(CCNode* node) { break; } } + float textureRect[4] = { + spriteNode->getTextureRect().getMinX(), + spriteNode->getTextureRect().getMinY(), + spriteNode->getTextureRect().size.width, + spriteNode->getTextureRect().size.height, + }; + if (ImGui::DragFloat4("Rect", textureRect), 0.03f) { + spriteNode->setTextureRect({textureRect[0], textureRect[1], textureRect[2], textureRect[3]}, spriteNode->isTextureRectRotated(), spriteNode->getContentSize()); + } + + bool isRectRotated = spriteNode->isTextureRectRotated();; + if (ImGui::Checkbox("Rotate Rect", &isRectRotated)) { + spriteNode->setTextureRect(spriteNode->getTextureRect(), isRectRotated, spriteNode->getContentSize()); + } + } + ImGui::NewLine(); + auto* cachedTextures = CCTextureCache::sharedTextureCache()->m_pTextures; + for (auto [key, obj] : CCDictionaryExt(cachedTextures)) { + if (obj == texture) { + std::string fileName = std::filesystem::path(key).filename().string(); + ImGui::TextWrapped("Texture name: %s", fileName.c_str()); + ImGui::TextWrapped("Texture path: %s", key.c_str()); + if (ImGui::Button(U8STR(FEATHER_COPY " Copy Texture Name##copytexturename"))) { + clipboard::write(fileName); + } + ImGui::SameLine(); + if (ImGui::Button(U8STR(FEATHER_COPY " Copy Texture Path##copytexturepath"))) { + clipboard::write(key); + } + break; + } } + } + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); + } + + if (auto menuItemNode = typeinfo_cast(node)) { + const auto selector = menuItemNode->m_pfnSelector; + if (!selector) { + std::string addr = "N/A"; + ImGui::Text("CCMenuItem selector: %s", addr.c_str()); + } else { + const auto addr = formatAddressIntoOffset(addresser::getNonVirtual(selector), true); + ImGui::Text("CCMenuItem selector: %s", addr.c_str()); + ImGui::SameLine(); + if (ImGui::Button(U8STR(FEATHER_COPY " Copy##copymenuitem"))) { + const auto addrNoModule = formatAddressIntoOffset(addresser::getNonVirtual(selector), false); + clipboard::write(addrNoModule); + } + } + if (ImGui::Button(U8STR(FEATHER_LINK " Activate##activatemenuitem"))) { + menuItemNode->activate(); + } + checkbox("Enabled##enabledmenuitem", menuItemNode, &CCMenuItem::isEnabled, &CCMenuItem::setEnabled); + if (auto menuItemSpriteExtra = typeinfo_cast(menuItemNode)) { + bool animationEnabled = menuItemSpriteExtra->m_animationEnabled; + if (ImGui::Checkbox("Animation Enabled##menuitemanimationenabled", &animationEnabled)) { + menuItemSpriteExtra->m_animationEnabled = animationEnabled; + } + float sizeMult = menuItemSpriteExtra->m_fSizeMult; + if (ImGui::DragFloat("Size Multiplier##menuitemsizemult", &sizeMult, .1f)) { + menuItemSpriteExtra->setSizeMult(sizeMult); + } + float scaleMultiplier = menuItemSpriteExtra->m_scaleMultiplier; + if (ImGui::DragFloat("Scale Multipler##menuitemscalemultiplier", &scaleMultiplier, .03f)) { + menuItemSpriteExtra->m_scaleMultiplier = scaleMultiplier; + } + float baseScale = menuItemSpriteExtra->m_baseScale; + if (ImGui::DragFloat("Base Scale##menuitembasescale", &baseScale, .03f)) { + menuItemSpriteExtra->m_baseScale = baseScale; + } } + + ImGui::NewLine(); + ImGui::Separator(); + ImGui::NewLine(); } - ImGui::NewLine(); - ImGui::Separator(); - ImGui::NewLine(); - if (auto rawOpts = node->getLayoutOptions()) { ImGui::Text("Layout options: %s", typeid(*rawOpts).name()); @@ -191,26 +319,207 @@ void DevTools::drawNodeAttributes(CCNode* node) { parent->updateLayout(); } } + ImGui::SameLine(); + if (ImGui::Button(U8STR(FEATHER_TRASH_2 " Remove Layout Options"))) { + node->setLayoutOptions(nullptr); + } + if (auto opts = typeinfo_cast(rawOpts)) { + bool updateLayout = false; + auto minRelativeScaleOpt = opts->getMinRelativeScale(); + float minRelativeScale = minRelativeScaleOpt.value_or(0); + bool hasMinRelativeScale = minRelativeScaleOpt.has_value(); + + if (ImGui::Checkbox("Has Min Relative Scale##simplescale0", &hasMinRelativeScale)) { + if (hasMinRelativeScale) { + opts->setMinRelativeScale(0); + } + else { + opts->setMinRelativeScale(std::nullopt); + } + updateLayout = true; + } + if (hasMinRelativeScale) { + if (ImGui::DragFloat("Min Relative Scale##simplescale1", &minRelativeScale)) { + + opts->setMinRelativeScale(minRelativeScale); + updateLayout = true; + } + } + + auto maxRelativeScaleOpt = opts->getMaxRelativeScale(); + float maxRelativeScale = maxRelativeScaleOpt.value_or(0); + bool hasMaxRelativeScale = maxRelativeScaleOpt.has_value(); + if (ImGui::Checkbox("Has Max Relative Scale##simplescale2", &hasMaxRelativeScale)) { + if (hasMaxRelativeScale) { + opts->setMaxRelativeScale(0); + } + else { + opts->setMaxRelativeScale(std::nullopt); + } + updateLayout = true; + } + + if (hasMaxRelativeScale) { + if (ImGui::DragFloat("Max Relative Scale##simplescale3", &maxRelativeScale)) { + opts->setMaxRelativeScale(maxRelativeScale); + updateLayout = true; + } + } + + int scalingPriority = static_cast(opts->getScalingPriority()); + + ImGui::Text("Scaling Priority"); + bool updateScalePrio = false; + updateScalePrio |= ImGui::RadioButton( + "First##scalepriooption1", &scalingPriority, static_cast(ScalingPriority::First) + ); + ImGui::SameLine(); + updateScalePrio |= ImGui::RadioButton( + "Early##scalepriooption2", &scalingPriority, static_cast(ScalingPriority::Early) + ); + ImGui::SameLine(); + updateScalePrio |= ImGui::RadioButton( + "Normal##scalepriooption3", &scalingPriority, static_cast(ScalingPriority::Normal) + ); + ImGui::SameLine(); + updateScalePrio |= ImGui::RadioButton( + "Late##scalepriooption4", &scalingPriority, static_cast(ScalingPriority::Late) + ); + ImGui::SameLine(); + updateScalePrio |= ImGui::RadioButton( + "Last##scalepriooption4", &scalingPriority, static_cast(ScalingPriority::Last) + ); + ImGui::SameLine(); + updateScalePrio |= ImGui::RadioButton( + "Never##alignmentoption0", &scalingPriority, static_cast(ScalingPriority::Never) + ); + if (updateScalePrio) { + opts->setScalingPriority(static_cast(scalingPriority)); + updateLayout = true; + } + + if (updateLayout && node->getParent()) { + node->getParent()->updateLayout(); + } + } if (auto opts = typeinfo_cast(rawOpts)) { bool updateLayout = false; ImGui::Text("Auto Scale"); auto updateAxis = false; - int autoScale = opts->getAutoScale() ? opts->getAutoScale().value() + 1 : 0; - updateAxis |= ImGui::RadioButton("Default", &autoScale, 0); + int autoScale = opts->getAutoScale().has_value() ? opts->getAutoScale().value() : -1; + updateAxis |= ImGui::RadioButton("Default##autoscale0", &autoScale, -1); ImGui::SameLine(); - updateAxis |= ImGui::RadioButton("Enable", &autoScale, 1); + updateAxis |= ImGui::RadioButton("Enable##autoscale1", &autoScale, 1); ImGui::SameLine(); - updateAxis |= ImGui::RadioButton("Disable", &autoScale, 2); + updateAxis |= ImGui::RadioButton("Disable##autoscale2", &autoScale, 0); if (updateAxis) { switch (autoScale) { - case 0: opts->setAutoScale(std::nullopt); break; + case -1: opts->setAutoScale(std::nullopt); break; + case 0: opts->setAutoScale(false); break; case 1: opts->setAutoScale(true); break; - case 2: opts->setAutoScale(false); break; } updateLayout = true; } + auto minScale = opts->getMinScale(); + bool hasMinScale = opts->hasExplicitMinScale(); + auto maxScale = opts->getMaxScale(); + bool hasMaxScale = opts->hasExplicitMaxScale(); + + if (ImGui::Checkbox("Has Min Scale", &hasMinScale)) { + if (hasMinScale) { + opts->setScaleLimits(minScale, hasMaxScale ? std::optional(maxScale) : std::nullopt); + } + else { + opts->setScaleLimits(std::nullopt, hasMaxScale ? std::optional(maxScale) : std::nullopt); + } + updateLayout = true; + } + if (hasMinScale) { + if (ImGui::DragFloat("Min Scale", &minScale)) { + opts->setScaleLimits(minScale, maxScale); + updateLayout = true; + } + } + + if (ImGui::Checkbox("Has Max Scale", &hasMaxScale)) { + if (hasMaxScale) { + opts->setScaleLimits(hasMinScale ? std::optional(minScale) : std::nullopt, maxScale); + } + else { + opts->setScaleLimits(hasMinScale ? std::optional(minScale) : std::nullopt, std::nullopt); + } + updateLayout = true; + } + + if (hasMaxScale) { + if (ImGui::DragFloat("Max Scale", &maxScale)) { + opts->setScaleLimits(minScale, maxScale); + updateLayout = true; + } + } + + auto lengthOpt = opts->getLength(); + float length = lengthOpt.value_or(0); + bool hasLength = lengthOpt.has_value(); + + if (ImGui::Checkbox("Has Length", &hasLength)) { + if (hasLength) { + opts->setLength(0); + } + else { + opts->setLength(std::nullopt); + } + updateLayout = true; + } + if (hasLength) { + if (ImGui::DragFloat("Length", &length)) { + opts->setLength(length); + updateLayout = true; + } + } + + auto prevGapOpt = opts->getPrevGap(); + float prevGap = prevGapOpt.value_or(0); + bool hasPrevGap = prevGapOpt.has_value(); + + if (ImGui::Checkbox("Has Prev Gap", &hasPrevGap)) { + if (hasPrevGap) { + opts->setPrevGap(0); + } + else { + opts->setPrevGap(std::nullopt); + } + updateLayout = true; + } + if (hasPrevGap) { + if (ImGui::DragFloat("Prev Gap", &prevGap)) { + opts->setPrevGap(prevGap); + updateLayout = true; + } + } + + auto nextGapOpt = opts->getNextGap(); + float nextGap = nextGapOpt.value_or(0); + bool hasNextGap = nextGapOpt.has_value(); + + if (ImGui::Checkbox("Has Next Gap", &hasNextGap)) { + if (hasNextGap) { + opts->setNextGap(0); + } + else { + opts->setNextGap(std::nullopt); + } + updateLayout = true; + } + if (hasNextGap) { + if (ImGui::DragFloat("Next Gap", &nextGap)) { + opts->setNextGap(nextGap); + updateLayout = true; + } + } + if (checkbox("Break Line", opts, AXIS_GET(BreakLine))) { updateLayout = true; } @@ -218,12 +527,60 @@ void DevTools::drawNodeAttributes(CCNode* node) { updateLayout = true; } + auto relativeScale = opts->getRelativeScale(); + if (ImGui::DragFloat("Relative Scale", &relativeScale)) { + opts->setRelativeScale(relativeScale); + updateLayout = true; + } + auto prio = opts->getScalePriority(); if (ImGui::DragInt("Scale Priority", &prio, .03f)) { opts->setScalePriority(prio); updateLayout = true; } + auto crossAxisAlignmentOpt = opts->getCrossAxisAlignment(); + int crossAxisAlignment = static_cast(crossAxisAlignmentOpt.value_or(AxisAlignment::Start)); + bool hasCrossAxisAlignment = crossAxisAlignmentOpt.has_value(); + + if (ImGui::Checkbox("Has Cross Axis Alignment", &hasCrossAxisAlignment)) { + if (hasCrossAxisAlignment) { + opts->setCrossAxisAlignment(AxisAlignment::Start); + } + else { + opts->setCrossAxisAlignment(std::nullopt); + } + updateLayout = true; + } + if (hasCrossAxisAlignment) { + + ImGui::Text("Cross Axis Alignment"); + bool updateAlignment = false; + updateAlignment |= ImGui::RadioButton( + "Start##alignmentoption0", &crossAxisAlignment, static_cast(AxisAlignment::Start) + ); + ImGui::SameLine(); + updateAlignment |= ImGui::RadioButton( + "Center##alignmentoption1", &crossAxisAlignment, static_cast(AxisAlignment::Center) + ); + ImGui::SameLine(); + updateAlignment |= ImGui::RadioButton( + "End##alignmentoption2", &crossAxisAlignment, static_cast(AxisAlignment::End) + ); + ImGui::SameLine(); + updateAlignment |= ImGui::RadioButton( + "Even##alignmentoption3", &crossAxisAlignment, static_cast(AxisAlignment::Even) + ); + ImGui::SameLine(); + updateAlignment |= ImGui::RadioButton( + "Between##alignmentoption4", &crossAxisAlignment, static_cast(AxisAlignment::Between) + ); + if (updateAlignment) { + opts->setCrossAxisAlignment(static_cast(crossAxisAlignment)); + updateLayout = true; + } + } + if (updateLayout && node->getParent()) { node->getParent()->updateLayout(); } @@ -268,6 +625,9 @@ void DevTools::drawNodeAttributes(CCNode* node) { } } else { + if (ImGui::Button(U8STR(FEATHER_PLUS " Add SimpleAxisLayoutOptions"))) { + node->setLayoutOptions(SimpleAxisLayoutOptions::create()); + } if (ImGui::Button(U8STR(FEATHER_PLUS " Add AxisLayoutOptions"))) { node->setLayoutOptions(AxisLayoutOptions::create()); } @@ -276,21 +636,6 @@ void DevTools::drawNodeAttributes(CCNode* node) { } } - ImGui::NewLine(); - ImGui::Separator(); - ImGui::NewLine(); - - if (auto delegate = typeinfo_cast(node)) { - if (auto handler = CCTouchDispatcher::get()->findHandler(delegate)) { - auto priority = handler->getPriority(); - - if (ImGui::DragInt("Touch Priority", &priority, .03f)) { - CCTouchDispatcher::get()->setPriority(priority, handler->getDelegate()); - } - } - } - - ImGui::NewLine(); ImGui::Separator(); ImGui::NewLine(); @@ -302,12 +647,253 @@ void DevTools::drawNodeAttributes(CCNode* node) { node->updateLayout(); } ImGui::SameLine(); + if (ImGui::Button(U8STR(FEATHER_TRASH_2 " Remove Layout"))) { + node->setLayout(nullptr); + } + ImGui::SameLine(); if (ImGui::Button(U8STR(FEATHER_PLUS " Add Test Child"))) { auto spr = CCSprite::create("GJ_button_01.png"); auto btn = CCMenuItemSpriteExtra::create(spr, node, nullptr); node->addChild(btn); node->updateLayout(); } + if (auto layout = typeinfo_cast(rawLayout)) { + ImGui::SameLine(); + if (ImGui::Button(U8STR(FEATHER_PLUS " Add AxisGap"))) { + node->addChild(AxisGap::create(5)); + node->updateLayout(); + } + bool updateLayout = false; + auto axis = static_cast(layout->getAxis()); + ImGui::Text("Axis"); + auto updateAxis = false; + updateAxis |= ImGui::RadioButton("Row", &axis, static_cast(Axis::Row)); + ImGui::SameLine(); + updateAxis |= ImGui::RadioButton("Column", &axis, static_cast(Axis::Column)); + if (updateAxis) { + if (layout->getAxis() != static_cast(axis)) { + node->setContentSize({ + node->getContentSize().height, + node->getContentSize().width + }); + } + layout->setAxis(static_cast(axis)); + updateLayout = true; + } + { + auto axisScaling = static_cast(layout->getMainAxisScaling()); + ImGui::Text("Main Axis Scaling"); + bool updateScaling = false; + updateScaling |= ImGui::RadioButton( + "None##mainscale0", &axisScaling, static_cast(AxisScaling::None) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Scale Down##mainscale1", &axisScaling, static_cast(AxisScaling::ScaleDown) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Scale##mainscale2", &axisScaling, static_cast(AxisScaling::Scale) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Grow##mainscale3", &axisScaling, static_cast(AxisScaling::Grow) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Fit##mainscale4", &axisScaling, static_cast(AxisScaling::Fit) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Scale Down Gaps##mainscale5", &axisScaling, static_cast(AxisScaling::ScaleDownGaps) + ); + if (updateScaling) { + layout->setMainAxisScaling(static_cast(axisScaling)); + updateLayout = true; + } + } + { + auto axisScaling = static_cast(layout->getCrossAxisScaling()); + ImGui::Text("Cross Axis Scaling"); + bool updateScaling = false; + updateScaling |= ImGui::RadioButton( + "None##crossscale0", &axisScaling, static_cast(AxisScaling::None) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Scale Down##crossscale1", &axisScaling, static_cast(AxisScaling::ScaleDown) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Scale##crossscale2", &axisScaling, static_cast(AxisScaling::Scale) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Grow##crossscale3", &axisScaling, static_cast(AxisScaling::Grow) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Fit##crossscale4", &axisScaling, static_cast(AxisScaling::Fit) + ); + ImGui::SameLine(); + updateScaling |= ImGui::RadioButton( + "Scale Down Gaps##crossscale5", &axisScaling, static_cast(AxisScaling::ScaleDownGaps) + ); + if (updateScaling) { + layout->setCrossAxisScaling(static_cast(axisScaling)); + updateLayout = true; + } + } + { + auto align = static_cast(layout->getMainAxisAlignment()); + ImGui::Text("Main Axis Alignment"); + bool updateAlign = false; + updateAlign |= ImGui::RadioButton( + "Start##mainalign0", &align, static_cast(MainAxisAlignment::Start) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "Center##mainalign1", &align, static_cast(MainAxisAlignment::Center) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "End##mainalign2", &align, static_cast(MainAxisAlignment::End) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "Even##mainalign3", &align, static_cast(MainAxisAlignment::Even) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "Between##mainalign4", &align, static_cast(MainAxisAlignment::Between) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "Around##mainalign5", &align, static_cast(MainAxisAlignment::Around) + ); + if (updateAlign) { + layout->setMainAxisAlignment(static_cast(align)); + updateLayout = true; + } + } + { + auto align = static_cast(layout->getCrossAxisAlignment()); + ImGui::Text("Cross Axis Alignment"); + bool updateAlign = false; + updateAlign |= ImGui::RadioButton( + "Start##crossalign0", &align, static_cast(CrossAxisAlignment::Start) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "Center##crossalign1", &align, static_cast(CrossAxisAlignment::Center) + ); + ImGui::SameLine(); + updateAlign |= ImGui::RadioButton( + "End##crossalign2", &align, static_cast(CrossAxisAlignment::End) + ); + if (updateAlign) { + layout->setCrossAxisAlignment(static_cast(align)); + updateLayout = true; + } + } + + std::string axisDirection = axis ? "Bottom to Top" : "Left to Right"; + std::string secondAxisDirection = axis ? "Top to Bottom" : "Right to Left"; + + std::string crossAxisDirection = !axis ? "Bottom to Top" : "Left to Right"; + std::string secondcrossAxisDirection = !axis ? "Top to Bottom" : "Right to Left"; + { + auto direction = static_cast(layout->getMainAxisDirection()); + ImGui::Text("Main Axis Direction"); + bool updateDirection = false; + + updateDirection |= ImGui::RadioButton( + axisDirection.c_str(), &direction, static_cast(AxisDirection::FrontToBack) + ); + ImGui::SameLine(); + updateDirection |= ImGui::RadioButton( + secondAxisDirection.c_str(), &direction, static_cast(AxisDirection::BackToFront) + ); + if (updateDirection) { + layout->setMainAxisDirection(static_cast(direction)); + updateLayout = true; + } + } + { + auto direction = static_cast(layout->getCrossAxisDirection()); + ImGui::Text("Cross Axis Direction"); + bool updateDirection = false; + updateDirection |= ImGui::RadioButton( + crossAxisDirection.c_str(), &direction, static_cast(AxisDirection::FrontToBack) + ); + ImGui::SameLine(); + updateDirection |= ImGui::RadioButton( + secondcrossAxisDirection.c_str(), &direction, static_cast(AxisDirection::BackToFront) + ); + if (updateDirection) { + layout->setCrossAxisDirection(static_cast(direction)); + updateLayout = true; + } + } + + auto gap = layout->getGap(); + if (ImGui::DragFloat("Gap", &gap)) { + layout->setGap(gap); + updateLayout = true; + } + + auto ignoreInvisibleChildren = layout->isIgnoreInvisibleChildren(); + if (ImGui::Checkbox("Ignore Invisible Children", &ignoreInvisibleChildren)) { + layout->ignoreInvisibleChildren(ignoreInvisibleChildren); + updateLayout = true; + } + + auto minRelativeScaleOpt = layout->getMinRelativeScale(); + float minRelativeScale = minRelativeScaleOpt.value_or(0); + bool hasMinRelativeScale = minRelativeScaleOpt.has_value(); + + if (ImGui::Checkbox("Has Min Relative Scale", &hasMinRelativeScale)) { + if (hasMinRelativeScale) { + layout->setMinRelativeScale(0); + } + else { + layout->setMinRelativeScale(std::nullopt); + } + updateLayout = true; + } + if (hasMinRelativeScale) { + if (ImGui::DragFloat("Min Relative Scale", &minRelativeScale)) { + + layout->setMinRelativeScale(minRelativeScale); + updateLayout = true; + + } + } + + auto maxRelativeScaleOpt = layout->getMaxRelativeScale(); + float maxRelativeScale = maxRelativeScaleOpt.value_or(0); + bool hasMaxRelativeScale = maxRelativeScaleOpt.has_value(); + if (ImGui::Checkbox("Has Max Relative Scale", &hasMaxRelativeScale)) { + if (hasMaxRelativeScale) { + layout->setMaxRelativeScale(0); + } + else { + layout->setMaxRelativeScale(std::nullopt); + } + updateLayout = true; + } + + if (hasMaxRelativeScale) { + if (ImGui::DragFloat("Max Relative Scale", &maxRelativeScale)) { + layout->setMaxRelativeScale(maxRelativeScale); + updateLayout = true; + } + } + + if (updateLayout) { + node->updateLayout(); + } + } if (auto layout = typeinfo_cast(rawLayout)) { bool updateLayout = false; @@ -344,23 +930,23 @@ void DevTools::drawNodeAttributes(CCNode* node) { ImGui::Text("Axis Alignment"); bool updateAlign = false; updateAlign |= ImGui::RadioButton( - "Start", &align, static_cast(AxisAlignment::Start) + "Start##axisalign0", &align, static_cast(AxisAlignment::Start) ); ImGui::SameLine(); updateAlign |= ImGui::RadioButton( - "Center", &align, static_cast(AxisAlignment::Center) + "Center##axisalign1", &align, static_cast(AxisAlignment::Center) ); ImGui::SameLine(); updateAlign |= ImGui::RadioButton( - "End", &align, static_cast(AxisAlignment::End) + "End##axisalign2", &align, static_cast(AxisAlignment::End) ); ImGui::SameLine(); updateAlign |= ImGui::RadioButton( - "Even", &align, static_cast(AxisAlignment::Even) + "Even##axisalign3", &align, static_cast(AxisAlignment::Even) ); ImGui::SameLine(); updateAlign |= ImGui::RadioButton( - "Between", &align, static_cast(AxisAlignment::Between) + "Between##axisalign4", &align, static_cast(AxisAlignment::Between) ); if (updateAlign) { layout->setAxisAlignment(static_cast(align)); @@ -432,6 +1018,12 @@ void DevTools::drawNodeAttributes(CCNode* node) { updateLayout = true; } + auto ignoreInvisibleChildren = layout->isIgnoreInvisibleChildren(); + if (ImGui::Checkbox("Ignore Invisible Children", &ignoreInvisibleChildren)) { + layout->ignoreInvisibleChildren(ignoreInvisibleChildren); + updateLayout = true; + } + auto autoScale = layout->getAutoScale(); if (ImGui::Checkbox("Auto Scale", &autoScale)) { layout->setAutoScale(autoScale); @@ -450,12 +1042,35 @@ void DevTools::drawNodeAttributes(CCNode* node) { updateLayout = true; } + auto maxAutoGrowAxisOpt = layout->getAutoGrowAxis(); + float autoGrowAxis = maxAutoGrowAxisOpt.value_or(0); + bool hasAutoGrowAxis = maxAutoGrowAxisOpt.has_value(); + if (ImGui::Checkbox("Has Auto Grow Axis", &hasAutoGrowAxis)) { + if (hasAutoGrowAxis) { + layout->setAutoGrowAxis(0); + } + else { + layout->setAutoGrowAxis(std::nullopt); + } + updateLayout = true; + } + + if (hasAutoGrowAxis) { + if (ImGui::DragFloat("Auto Grow Axis", &autoGrowAxis)) { + layout->setAutoGrowAxis(autoGrowAxis); + updateLayout = true; + } + } + if (updateLayout) { node->updateLayout(); } } } else { + if (ImGui::Button(U8STR(FEATHER_PLUS " Add SimpleAxisLayout"))) { + node->setLayout(SimpleAxisLayout::create(Axis::Row)); + } if (ImGui::Button(U8STR(FEATHER_PLUS " Add AxisLayout"))) { node->setLayout(AxisLayout::create()); } diff --git a/src/pages/GeometryDash.cpp b/src/pages/GeometryDash.cpp index dad85a1..3db9142 100644 --- a/src/pages/GeometryDash.cpp +++ b/src/pages/GeometryDash.cpp @@ -165,6 +165,103 @@ void drawLayoutArrows( ); } +AxisAlignment translateToAxisAlignment(const MainAxisAlignment& alignment) { + switch (alignment) { + case MainAxisAlignment::Start: return AxisAlignment::Start; + case MainAxisAlignment::Between: return AxisAlignment::Between; + case MainAxisAlignment::Even: return AxisAlignment::Even; + case MainAxisAlignment::Center: return AxisAlignment::Center; + case MainAxisAlignment::Around: + case MainAxisAlignment::End: return AxisAlignment::End; + } + return AxisAlignment::Start; +} + +AxisAlignment translateToAxisAlignment(const CrossAxisAlignment& alignment) { + switch (alignment) { + case CrossAxisAlignment::Start: return AxisAlignment::Start; + case CrossAxisAlignment::Center: return AxisAlignment::Center; + case CrossAxisAlignment::End: return AxisAlignment::End; + } + return AxisAlignment::Start; +} + +void drawLayoutArrows( + ImDrawList& foreground, + SimpleAxisLayout* layout, + ImVec2 const& tmax, ImVec2 const& tmin +) { + auto row = layout->getAxis() == Axis::Row; + + float x; + float y; + + // cheating + AxisAlignment mainAxisAlignment = translateToAxisAlignment(layout->getMainAxisAlignment()); + AxisAlignment crossAxisAlignment = translateToAxisAlignment(layout->getCrossAxisAlignment()); + + switch (row ? mainAxisAlignment : crossAxisAlignment) { + case AxisAlignment::Start: + case AxisAlignment::Between: + case AxisAlignment::Even: x = tmin.x; break; + case AxisAlignment::Center: x = tmin.x + (tmax.x - tmin.x) / 2; break; + case AxisAlignment::End: x = tmax.x; break; + } + switch (row ? crossAxisAlignment : mainAxisAlignment) { + case AxisAlignment::Start: + case AxisAlignment::Between: + case AxisAlignment::Even: y = tmin.y; break; + case AxisAlignment::Center: y = tmin.y + (tmax.y - tmin.y) / 2; break; + case AxisAlignment::End: y = tmax.y; break; + } + + bool mainReverse = layout->getMainAxisDirection() == static_cast(1); + bool crossReverse = layout->getCrossAxisDirection() == static_cast(1); + + bool growCrossAxis = layout->getCrossAxisScaling() == AxisScaling::Grow; + + if (row) { + drawRowAxisArrow( + foreground, + x, y, tmax, tmin, + mainAxisAlignment, + mainReverse, + IM_COL32(255, 55, 55, 255) + ); + if (growCrossAxis) { + drawColAxisArrow( + foreground, + x, y, tmax, tmin, + crossAxisAlignment, + crossReverse, + IM_COL32(55, 55, 255, 255) + ); + } + } + else { + drawColAxisArrow( + foreground, + x, y, tmax, tmin, + mainAxisAlignment, + !mainReverse, + IM_COL32(255, 55, 55, 255) + ); + if (growCrossAxis) { + drawRowAxisArrow( + foreground, + x, y, tmax, tmin, + crossAxisAlignment, + !crossReverse, + IM_COL32(55, 55, 255, 255) + ); + } + } + + foreground.AddCircleFilled( + ImVec2(x, y), 6.f, IM_COL32(255, 55, 55, 255) + ); +} + void DevTools::drawHighlight(CCNode* node, HighlightMode mode) { auto& foreground = *ImGui::GetWindowDrawList(); auto parent = node->getParent(); @@ -231,6 +328,9 @@ void DevTools::drawHighlight(CCNode* node, HighlightMode mode) { if (auto layout = typeinfo_cast(node->getLayout())) { drawLayoutArrows(foreground, layout, tmax, tmin); } + if (auto layout = typeinfo_cast(node->getLayout())) { + drawLayoutArrows(foreground, layout, tmax, tmin); + } } break; case HighlightMode::Layout: { @@ -242,6 +342,9 @@ void DevTools::drawHighlight(CCNode* node, HighlightMode mode) { if (auto layout = typeinfo_cast(node->getLayout())) { drawLayoutArrows(foreground, layout, tmax, tmin); } + if (auto layout = typeinfo_cast(node->getLayout())) { + drawLayoutArrows(foreground, layout, tmax, tmin); + } } break; default: @@ -327,3 +430,46 @@ void DevTools::drawGD(GLRenderCtx* gdCtx) { ImGui::End(); } } + +// cheaty way to detect resize and update the render +class ResizeWatcher : public CCObject { +private: + int m_lastWidth; + int m_lastHeight; + float m_resizeTimer; + bool m_hasPendingResize; +public: + static ResizeWatcher* create() { + auto ret = new ResizeWatcher(); + ret->autorelease(); + return ret; + } + + void update(float dt) { + auto view = CCDirector::sharedDirector()->getOpenGLView(); + int w = view->getFrameSize().width; + int h = view->getFrameSize().height; + + if (w != m_lastWidth || h != m_lastHeight) { + m_lastWidth = w; + m_lastHeight = h; + m_resizeTimer = 0.0f; + m_hasPendingResize = true; + return; + } + + if (m_hasPendingResize) { + m_resizeTimer += dt; + if (m_resizeTimer > 0.2f) { + m_hasPendingResize = false; + shouldUpdateGDRenderBuffer() = true; + } + } + } +}; + +$execute { + Loader::get()->queueInMainThread([]{ + CCScheduler::get()->scheduleUpdateForTarget(ResizeWatcher::create(), INT_MAX, false); + }); +} \ No newline at end of file diff --git a/src/pages/Settings.cpp b/src/pages/Settings.cpp index b4e4cd4..20e852f 100644 --- a/src/pages/Settings.cpp +++ b/src/pages/Settings.cpp @@ -70,22 +70,29 @@ void DevTools::drawSettings() { "Shows the memory viewer window." ); } + ImGui::Checkbox("Show Mod Graph", &m_settings.showModGraph); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip( + "Shows the mod graph window." + ); + } ImGui::PopStyleVar(); ImGui::Separator(); ImGui::DragFloat("Font Size", &ImGui::GetIO().FontGlobalScale, 0.01f, 1.0f, 3.0f); + +#ifdef GEODE_IS_DESKTOP ImGui::Separator(); ImGui::Text("GD Window"); - // TODO: undo later -#if 0 auto winSize = CCDirector::get()->getWinSize(); auto frameSize = GameManager::get()->resolutionForKey(GameManager::get()->m_resolution); auto fps = roundf(1 / CCDirector::get()->getAnimationInterval()); auto ratio = std::gcd(static_cast(frameSize.width), static_cast(frameSize.height)); +#ifdef GEODE_IS_WINDOWS std::string text = ""; text += "Custom"; @@ -103,7 +110,6 @@ void DevTools::drawSettings() { GameManager::get()->m_resolution = selectedResolution; // TODO: idk how to do this on macos - #ifdef GEODE_IS_WINDOWS if (selectedResolution != 0) { auto size = GameManager::get()->resolutionForKey(selectedResolution); CCEGLView::get()->resizeWindow(size.width, size.height); @@ -112,7 +118,6 @@ void DevTools::drawSettings() { CCEGLView::get()->resizeWindow(customResolution.width, customResolution.height); } CCEGLView::get()->centerWindow(); - #endif } if (selectedResolution == 0) { @@ -125,14 +130,14 @@ void DevTools::drawSettings() { size[1] = std::fabs(size[1]); customResolution = CCSizeMake(size[0], size[1]); } - #ifdef GEODE_IS_WINDOWS if (ImGui::Button("Apply##size-apply")) { GameManager::get()->m_resolution = 0; CCEGLView::get()->resizeWindow(customResolution.width, customResolution.height); CCEGLView::get()->centerWindow(); } - #endif } +#endif + ImGui::TextWrapped( "GL Size: %dx%d", @@ -150,7 +155,9 @@ void DevTools::drawSettings() { static_cast(frameSize.width / ratio), static_cast(frameSize.height / ratio) ); +#endif +#if 0 static Ref PAUSED_TARGETS = nullptr; if (ImGui::Button(m_pauseGame ? "Resume Game" : "Pause Game")) { m_pauseGame ^= 1; @@ -179,11 +186,26 @@ void DevTools::drawSettings() { ImGui::SetTooltip("Select Theme"); } + if (m_settings.theme == "Dark") { + auto color = m_settings.themeColor; + float _color[4] = { color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f }; + if (ImGui::ColorEdit4("Primary Color", _color)) { + color.r = _color[0] * 255; + color.g = _color[1] * 255; + color.b = _color[2] * 255; + color.a = _color[3] * 255; + m_settings.themeColor = color; + m_reloadTheme = true; + } + } + ImGui::Separator(); ImGui::TextWrapped("Developed by "); - RAINBOW_HUE += 0.01f; + float dt = CCDirector::get()->getDeltaTime(); + + RAINBOW_HUE += 0.25f * dt; if (RAINBOW_HUE >= 1.f) { RAINBOW_HUE = 0.f; } diff --git a/src/pages/Tree.cpp b/src/pages/Tree.cpp index 681ae5c..51f1faa 100644 --- a/src/pages/Tree.cpp +++ b/src/pages/Tree.cpp @@ -1,31 +1,13 @@ #include "../fonts/FeatherIcons.hpp" #include #include "../DevTools.hpp" +#include "../platform/utils.hpp" #ifndef GEODE_IS_WINDOWS #include #endif using namespace geode::prelude; -std::string getNodeName(CCObject* node) { -#ifdef GEODE_IS_WINDOWS - return typeid(*node).name() + 6; -#else - { - std::string ret; - - int status = 0; - auto demangle = abi::__cxa_demangle(typeid(*node).name(), 0, 0, &status); - if (status == 0) { - ret = demangle; - } - free(demangle); - - return ret; - } -#endif -} - void DevTools::drawTreeBranch(CCNode* node, size_t index) { auto selected = DevTools::get()->getSelectedNode() == node; @@ -58,7 +40,7 @@ void DevTools::drawTreeBranch(CCNode* node, size_t index) { DevTools::get()->selectNode(node); selected = true; } - if (ImGui::IsItemHovered() && (m_settings.alwaysHighlight || ImGui::IsKeyDown(ImGuiMod_Shift))) { + if (ImGui::IsItemHovered() && (m_settings.alwaysHighlight || ImGui::IsKeyDown(ImGuiMod_Shift))) { DevTools::get()->highlightNode(node, HighlightMode::Hovered); } if (expanded) { diff --git a/src/platform/utils.hpp b/src/platform/utils.hpp index 6653d53..6a23e99 100644 --- a/src/platform/utils.hpp +++ b/src/platform/utils.hpp @@ -2,6 +2,27 @@ #include #include +#ifndef GEODE_IS_WINDOWS +#include +#endif +#include + +static inline std::string getNodeName(cocos2d::CCObject* node) { +#ifdef GEODE_IS_WINDOWS + return typeid(*node).name() + 6; +#else + std::string ret; + + int status = 0; + auto demangle = abi::__cxa_demangle(typeid(*node).name(), 0, 0, &status); + if (status == 0) { + ret = demangle; + } + free(demangle); + + return ret; +#endif +} std::string formatAddressIntoOffset(uintptr_t addr, bool module); diff --git a/src/themes.cpp b/src/themes.cpp index 0d1ea84..c194d6d 100644 --- a/src/themes.cpp +++ b/src/themes.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "DevTools.hpp" using namespace geode::prelude; @@ -28,6 +29,33 @@ namespace { }; } + constexpr ImVec4 colorFromCocos(const ccColor4B& color) { + return { + color.r / 255.f, + color.g / 255.f, + color.b / 255.f, + color.a / 255.f + }; + } + + constexpr ImVec4 lighterColor(ImVec4 color, float factor = 1.2f) { + return ImVec4{ + (color.x * factor > 1.0f ? 1.0f : color.x * factor), + (color.y * factor > 1.0f ? 1.0f : color.y * factor), + (color.z * factor > 1.0f ? 1.0f : color.z * factor), + color.w + }; + } + + constexpr ImVec4 darkerColor(ImVec4 color, float factor = 0.6f) { + return ImVec4{ + color.x * factor, + color.y * factor, + color.z * factor, + color.w + }; + } + constexpr ImVec4 operator+(ImVec4 const& vec, int value) { return { clamp(vec.x + value / 255.f), @@ -73,64 +101,70 @@ namespace { } } -constexpr ImVec4 DARK_THEME_BG = color(33, 33, 33, 255); -constexpr ImVec4 DARK_THEME_BG_DARK = color(22, 22, 22, 255); -constexpr ImVec4 DARK_THEME_TEXT = color(255, 255, 255, 255); -constexpr ImVec4 DARK_THEME_LIGHT = color(255, 255, 255, 255); -constexpr ImVec4 DARK_THEME_PRIMARY = color(2, 119, 189, 255); -constexpr ImVec4 DARK_THEME_PRIMARY_DARK = color(2, 66, 104, 255); -constexpr ImVec4 DARK_THEME_PRIMARY_LIGHT = color(73, 164, 216, 255); -ThemeDef DARK_THEME_DEF { - .text = DARK_THEME_TEXT, - .textDisabled = DARK_THEME_TEXT - 102, - .textSelectedBG = DARK_THEME_PRIMARY - 60 - alpha(180), - .windowBG = DARK_THEME_BG, - .childBG = DARK_THEME_BG, - .popupBG = DARK_THEME_BG, - .border = DARK_THEME_BG + 23, - .borderShadow = DARK_THEME_BG + 50, - .frameBG = DARK_THEME_BG_DARK, - .frameBGHovered = DARK_THEME_BG_DARK + 20, - .frameBGActive = DARK_THEME_BG_DARK, - .titleBarBG = DARK_THEME_PRIMARY_DARK, - .titleBarBGCollapsed = DARK_THEME_PRIMARY_DARK - alpha(150), - .titleBarBGActive = DARK_THEME_PRIMARY, - .menuBarBG = DARK_THEME_BG_DARK, - .scrollbarBG = DARK_THEME_BG_DARK, - .scrollbarGrab = DARK_THEME_PRIMARY, - .scrollbarGrabHovered = DARK_THEME_PRIMARY_LIGHT, - .scrollbarGrabActive = DARK_THEME_PRIMARY_DARK, - .checkMark = DARK_THEME_PRIMARY, - .sliderGrab = DARK_THEME_PRIMARY, - .sliderGrabActive = DARK_THEME_PRIMARY_DARK, - .button = DARK_THEME_PRIMARY, - .buttonHovered = DARK_THEME_PRIMARY_LIGHT, - .buttonActive = DARK_THEME_PRIMARY_DARK, - .header = DARK_THEME_PRIMARY - alpha(140), - .headerHovered = DARK_THEME_PRIMARY_LIGHT - alpha(40), - .headerActive = DARK_THEME_PRIMARY_DARK, - .separator = DARK_THEME_LIGHT - alpha(140), - .separatorHovered = DARK_THEME_LIGHT - alpha(40), - .separatorActive = DARK_THEME_PRIMARY_DARK, - .resizeGrip = DARK_THEME_LIGHT - alpha(200), - .resizeGripHovered = DARK_THEME_LIGHT - alpha(100), - .resizeGripActive = DARK_THEME_PRIMARY_DARK, - .plotLines = DARK_THEME_PRIMARY - alpha(140), - .plotLinesHovered = DARK_THEME_PRIMARY - alpha(40), - .plotHistogram = DARK_THEME_PRIMARY - alpha(140), - .plotHistogramHovered = DARK_THEME_PRIMARY - alpha(40), - .dragDropTarget = DARK_THEME_PRIMARY - 60 - alpha(20), - .navHighlight = DARK_THEME_PRIMARY - 30 - alpha(40), - .navWindowingHighlight = DARK_THEME_PRIMARY - 40 - alpha(40), - .tab = DARK_THEME_PRIMARY - alpha(200), - .tabHovered = DARK_THEME_PRIMARY, - .tabActive = DARK_THEME_PRIMARY_LIGHT, - .tabUnfocused = DARK_THEME_PRIMARY_DARK - alpha(140), - .tabUnfocusedActive = DARK_THEME_PRIMARY - alpha(140), - .tableBorderStrong = DARK_THEME_LIGHT - alpha(50), - .tableBorderLight = DARK_THEME_LIGHT - alpha(100), -}; + +ThemeDef createDarkTheme() { + + constexpr ImVec4 DARK_THEME_BG = color(33, 33, 33, 255); + constexpr ImVec4 DARK_THEME_BG_DARK = color(22, 22, 22, 255); + constexpr ImVec4 DARK_THEME_TEXT = color(255, 255, 255, 255); + constexpr ImVec4 DARK_THEME_LIGHT = color(255, 255, 255, 255); + ImVec4 DARK_THEME_PRIMARY = colorFromCocos(DevTools::get()->getSettings().themeColor); + ImVec4 DARK_THEME_PRIMARY_DARK = darkerColor(DARK_THEME_PRIMARY); + ImVec4 DARK_THEME_PRIMARY_LIGHT = lighterColor(DARK_THEME_PRIMARY); + + ThemeDef DARK_THEME_DEF { + .text = DARK_THEME_TEXT, + .textDisabled = DARK_THEME_TEXT - 102, + .textSelectedBG = DARK_THEME_PRIMARY - 60 - alpha(180), + .windowBG = DARK_THEME_BG, + .childBG = DARK_THEME_BG, + .popupBG = DARK_THEME_BG, + .border = DARK_THEME_BG + 23, + .borderShadow = DARK_THEME_BG + 50, + .frameBG = DARK_THEME_BG_DARK, + .frameBGHovered = DARK_THEME_BG_DARK + 20, + .frameBGActive = DARK_THEME_BG_DARK, + .titleBarBG = DARK_THEME_PRIMARY_DARK, + .titleBarBGCollapsed = DARK_THEME_PRIMARY_DARK - alpha(150), + .titleBarBGActive = DARK_THEME_PRIMARY, + .menuBarBG = DARK_THEME_BG_DARK, + .scrollbarBG = DARK_THEME_BG_DARK, + .scrollbarGrab = DARK_THEME_PRIMARY, + .scrollbarGrabHovered = DARK_THEME_PRIMARY_LIGHT, + .scrollbarGrabActive = DARK_THEME_PRIMARY_DARK, + .checkMark = DARK_THEME_PRIMARY, + .sliderGrab = DARK_THEME_PRIMARY, + .sliderGrabActive = DARK_THEME_PRIMARY_DARK, + .button = DARK_THEME_PRIMARY, + .buttonHovered = DARK_THEME_PRIMARY_LIGHT, + .buttonActive = DARK_THEME_PRIMARY_DARK, + .header = DARK_THEME_PRIMARY - alpha(140), + .headerHovered = DARK_THEME_PRIMARY_LIGHT - alpha(40), + .headerActive = DARK_THEME_PRIMARY_DARK, + .separator = DARK_THEME_LIGHT - alpha(140), + .separatorHovered = DARK_THEME_LIGHT - alpha(40), + .separatorActive = DARK_THEME_PRIMARY_DARK, + .resizeGrip = DARK_THEME_LIGHT - alpha(200), + .resizeGripHovered = DARK_THEME_LIGHT - alpha(100), + .resizeGripActive = DARK_THEME_PRIMARY_DARK, + .plotLines = DARK_THEME_PRIMARY - alpha(140), + .plotLinesHovered = DARK_THEME_PRIMARY - alpha(40), + .plotHistogram = DARK_THEME_PRIMARY - alpha(140), + .plotHistogramHovered = DARK_THEME_PRIMARY - alpha(40), + .dragDropTarget = DARK_THEME_PRIMARY - 60 - alpha(20), + .navHighlight = DARK_THEME_PRIMARY - 30 - alpha(40), + .navWindowingHighlight = DARK_THEME_PRIMARY - 40 - alpha(40), + .tab = DARK_THEME_PRIMARY - alpha(200), + .tabHovered = DARK_THEME_PRIMARY, + .tabActive = DARK_THEME_PRIMARY_LIGHT, + .tabUnfocused = DARK_THEME_PRIMARY_DARK - alpha(140), + .tabUnfocusedActive = DARK_THEME_PRIMARY - alpha(140), + .tableBorderStrong = DARK_THEME_LIGHT - alpha(50), + .tableBorderLight = DARK_THEME_LIGHT - alpha(100), + }; + return DARK_THEME_DEF; +} constexpr ImVec4 MATERIAL_DARK_THEME_BG = color(33, 33, 33, 255); constexpr ImVec4 MATERIAL_DARK_THEME_BG_DARK = color(22, 22, 22, 255); @@ -279,20 +313,20 @@ void applyCommon(ImGuiStyle& style) { // style.ColorButtonPosition = ImGuiDir_Left; } -ThemeDef& getThemeDef(std::string const& name) { +ThemeDef getThemeDef(std::string const& name) { switch (hash(name.c_str())) { - case hash(DARK_THEME): return DARK_THEME_DEF; + case hash(DARK_THEME): return createDarkTheme(); case hash(MATERIAL_DARK_THEME): return MATERIAL_DARK_THEME_DEF; case hash(LIGHT_THEME): return LIGHT_THEME_DEF; } - return DARK_THEME_DEF; + return createDarkTheme(); } void applyTheme(std::string const& name) { auto& style = ImGui::GetStyle(); applyCommon(style); - auto& theme = getThemeDef(name); + auto theme = getThemeDef(name); style.Colors[ImGuiCol_Text] = theme.text; style.Colors[ImGuiCol_TextDisabled] = theme.textDisabled; style.Colors[ImGuiCol_TextSelectedBg] = theme.textSelectedBG; @@ -336,9 +370,9 @@ void applyTheme(std::string const& name) { style.Colors[ImGuiCol_NavWindowingHighlight] = theme.navWindowingHighlight; style.Colors[ImGuiCol_Tab] = theme.tab; style.Colors[ImGuiCol_TabHovered] = theme.tabHovered; - style.Colors[ImGuiCol_TabSelected] = theme.tabActive; - style.Colors[ImGuiCol_TabDimmed] = theme.tabUnfocused; - style.Colors[ImGuiCol_TabDimmedSelected] = theme.tabUnfocusedActive; + style.Colors[ImGuiCol_TabSelected] = theme.tabActive; + style.Colors[ImGuiCol_TabDimmed] = theme.tabUnfocused; + style.Colors[ImGuiCol_TabDimmedSelected] = theme.tabUnfocusedActive; style.Colors[ImGuiCol_TableBorderStrong] = theme.tableBorderStrong; style.Colors[ImGuiCol_TableBorderLight] = theme.tableBorderLight; } diff --git a/src/themes.hpp b/src/themes.hpp index 1e4910a..3448115 100644 --- a/src/themes.hpp +++ b/src/themes.hpp @@ -59,7 +59,7 @@ constexpr const char* DARK_THEME = "Dark"; constexpr const char* MATERIAL_DARK_THEME = "Material Dark"; constexpr const char* LIGHT_THEME = "Light"; -ThemeDef& getThemeDef(std::string const& theme); +ThemeDef getThemeDef(std::string const& theme); void applyTheme(std::string const& theme); std::vector getThemeOptions();