This commit is contained in:
altalk23 2024-01-20 23:58:39 +03:00
commit 1cf90c6acd
6 changed files with 150 additions and 84 deletions

View file

@ -224,6 +224,7 @@ protected:
bool m_crossReverse = false; bool m_crossReverse = false;
bool m_allowCrossAxisOverflow = true; bool m_allowCrossAxisOverflow = true;
bool m_growCrossAxis = false; bool m_growCrossAxis = false;
std::optional<float> m_autoGrowAxisMinLength;
struct Row; struct Row;
@ -279,6 +280,7 @@ public:
bool getAutoScale() const; bool getAutoScale() const;
bool getGrowCrossAxis() const; bool getGrowCrossAxis() const;
bool getCrossAxisOverflow() const; bool getCrossAxisOverflow() const;
std::optional<float> getAutoGrowAxis() const;
AxisLayout* setAxis(Axis axis); AxisLayout* setAxis(Axis axis);
/** /**
@ -325,6 +327,12 @@ public:
* automatically adjusted to fit the children * automatically adjusted to fit the children
*/ */
AxisLayout* setCrossAxisOverflow(bool allow); AxisLayout* setCrossAxisOverflow(bool allow);
/**
* If not `std::nullopt`, then the axis will be automatically extended to
* fit all items in a single row whose minimum length is the specified.
* Useful for scrollable list layer contents
*/
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
}; };
/** /**

View file

@ -750,10 +750,14 @@ void AxisLayout::apply(CCNode* on) {
std::pair<int, int> minMaxPrio; std::pair<int, int> minMaxPrio;
bool doAutoScale = false; bool doAutoScale = false;
float totalLength = 0;
AxisLayoutOptions const* prev = nullptr;
bool first = true; bool first = true;
for (auto node : CCArrayExt<CCNode*>(nodes)) { for (auto node : CCArrayExt<CCNode*>(nodes)) {
int prio = 0; int prio = 0;
if (auto opts = axisOpts(node)) { auto opts = axisOpts(node);
if (opts) {
prio = opts->getScalePriority(); prio = opts->getScalePriority();
// this does cause a recheck of m_autoScale every iteration but it // this does cause a recheck of m_autoScale every iteration but it
// should be pretty fast and this correctly handles the situation // should be pretty fast and this correctly handles the situation
@ -780,6 +784,22 @@ void AxisLayout::apply(CCNode* on) {
minMaxPrio.second = prio; minMaxPrio.second = prio;
} }
} }
if (m_autoGrowAxisMinLength.has_value()) {
totalLength += nodeAxis(node, m_axis, 1.f).axisLength + this->nextGap(prev, opts);
prev = opts;
}
}
if (m_autoGrowAxisMinLength.has_value()) {
if (totalLength < m_autoGrowAxisMinLength.value()) {
totalLength = m_autoGrowAxisMinLength.value();
}
if (m_axis == Axis::Row) {
on->setContentSize({ totalLength, on->getContentSize().height });
}
else {
on->setContentSize({ on->getContentSize().width, totalLength });
}
} }
this->tryFitLayout( this->tryFitLayout(
@ -855,6 +875,10 @@ bool AxisLayout::getCrossAxisOverflow() const {
return m_allowCrossAxisOverflow; return m_allowCrossAxisOverflow;
} }
std::optional<float> AxisLayout::getAutoGrowAxis() const {
return m_autoGrowAxisMinLength;
}
AxisLayout* AxisLayout::setAxis(Axis axis) { AxisLayout* AxisLayout::setAxis(Axis axis) {
m_axis = axis; m_axis = axis;
return this; return this;
@ -905,6 +929,11 @@ AxisLayout* AxisLayout::setGrowCrossAxis(bool shrink) {
return this; return this;
} }
AxisLayout* AxisLayout::setAutoGrowAxis(std::optional<float> allowAndMinLength) {
m_autoGrowAxisMinLength = allowAndMinLength;
return this;
}
AxisLayout* AxisLayout::create(Axis axis) { AxisLayout* AxisLayout::create(Axis axis) {
auto ret = new AxisLayout(axis); auto ret = new AxisLayout(axis);
ret->autorelease(); ret->autorelease();

View file

@ -36,23 +36,6 @@ static std::optional<int> fuzzyMatch(std::string const& kw, std::string const& s
return std::nullopt; return std::nullopt;
} }
#define WEIGHTED_MATCH(str_, weight_) \
if (auto match = fuzzyMatch(query.keywords.value(), str_)) {\
weighted += match.value() * weight_; \
someMatched = true; \
}
#define WEIGHTED_MATCH_MAX(str_, weight_) \
if (auto match = fuzzyMatch(query.keywords.value(), str_)) { \
weighted = std::max<double>(match.value() * weight_, weighted); \
someMatched = true; \
}
#define WEIGHTED_MATCH_ADD(str_, weight_) \
if (auto match = fuzzyMatch(query.keywords.value(), str_)) {\
weighted += match.value() * weight_; \
}
static std::optional<int> queryMatchKeywords( static std::optional<int> queryMatchKeywords(
ModListQuery const& query, ModListQuery const& query,
ModMetadata const& metadata ModMetadata const& metadata
@ -62,11 +45,17 @@ static std::optional<int> queryMatchKeywords(
// fuzzy match keywords // fuzzy match keywords
if (query.keywords) { if (query.keywords) {
bool someMatched = false; bool someMatched = false;
WEIGHTED_MATCH_MAX(metadata.getName(), 2); auto weightedMatch = [&](std::string const& str, double weight) {
WEIGHTED_MATCH_MAX(metadata.getID(), 1); if (auto match = fuzzyMatch(query.keywords.value(), str)) {
WEIGHTED_MATCH_MAX(metadata.getDeveloper(), 0.5); weighted = std::max<double>(match.value() * weight, weighted);
WEIGHTED_MATCH_MAX(metadata.getDetails().value_or(""), 0.05); someMatched = true;
WEIGHTED_MATCH_MAX(metadata.getDescription().value_or(""), 0.2); }
};
weightedMatch(metadata.getName(), 2);
weightedMatch(metadata.getID(), 1);
weightedMatch(metadata.getDeveloper(), 0.5);
weightedMatch(metadata.getDetails().value_or(""), 0.05);
weightedMatch(metadata.getDescription().value_or(""), 0.2);
if (!someMatched) { if (!someMatched) {
return std::nullopt; return std::nullopt;
} }
@ -128,7 +117,9 @@ static std::optional<int> queryMatch(ModListQuery const& query, IndexItemHandle
auto weighted = match.value(); auto weighted = match.value();
// add extra weight on tag matches // add extra weight on tag matches
if (query.keywords) { if (query.keywords) {
WEIGHTED_MATCH_ADD(ranges::join(item->getTags(), " "), 1.4); if (auto match = fuzzyMatch(query.keywords.value(), ranges::join(item->getTags(), " "))) {
weighted += match.value() * 1.4;
}
} }
// add extra weight to featured items to keep power consolidated in the // add extra weight to featured items to keep power consolidated in the
// hands of the rich Geode bourgeoisie // hands of the rich Geode bourgeoisie

View file

@ -16,7 +16,6 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
this->setTitle("Search Filters"); this->setTitle("Search Filters");
auto winSize = CCDirector::sharedDirector()->getWinSize(); auto winSize = CCDirector::sharedDirector()->getWinSize();
auto pos = CCPoint { winSize.width / 2 - 140.f, winSize.height / 2 + 45.f - iosAndAndroidSize * 0.25f };
// platforms // platforms
@ -26,22 +25,35 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
platformTitle->setScale(.5f); platformTitle->setScale(.5f);
m_mainLayer->addChild(platformTitle); m_mainLayer->addChild(platformTitle);
auto platformBG = CCScale9Sprite::create("square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }); auto platformsContainerBG = CCScale9Sprite::create("square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f });
platformBG->setColor({ 0, 0, 0 }); platformsContainerBG->setColor({ 0, 0, 0 });
platformBG->setOpacity(90); platformsContainerBG->setOpacity(90);
platformBG->setContentSize({ 290.f, 205.f - iosAndAndroidSize * 2.f }); platformsContainerBG->setContentSize({ 290.f, 205.f - iosAndAndroidSize * 2.f });
platformBG->setAnchorPoint({ 0.5f, 1.f }); platformsContainerBG->setAnchorPoint({ 0.5f, 1.f });
platformBG->setPosition(winSize.width / 2 - 85.f, winSize.height / 2 + 62.25f - iosAndAndroidSize * 0.25f); platformsContainerBG->setPosition(winSize.width / 2 - 85.f, winSize.height / 2 + 62.25f - iosAndAndroidSize * 0.25f);
platformBG->setScale(.5f); platformsContainerBG->setScale(.5f);
m_mainLayer->addChild(platformBG); m_mainLayer->addChild(platformsContainerBG);
m_platformsContainer = CCNode::create();
m_platformsContainer->setAnchorPoint({ 0.5f, 1.f });
m_platformsContainer->setContentSize(platformsContainerBG->getScaledContentSize());
m_platformsContainer->setPosition(platformsContainerBG->getPosition());
m_platformsContainer->setLayout(
ColumnLayout::create()
->setAxisReverse(true)
->setCrossAxisOverflow(true)
);
m_mainLayer->addChild(m_platformsContainer);
// TODO: add scrolllayer // TODO: add scrolllayer
this->enable(this->addPlatformToggle("Windows", PlatformID::Windows, pos), type); this->enable(this->addPlatformToggle("Windows", PlatformID::Windows), type);
this->enable(this->addPlatformToggle("macOS", PlatformID::MacOS, pos), type); this->enable(this->addPlatformToggle("macOS", PlatformID::MacOS), type);
//this->enable(this->addPlatformToggle("IOS", PlatformID::iOS, pos), type); //this->enable(this->addPlatformToggle("IOS", PlatformID::iOS), type);
this->enable(this->addPlatformToggle("Android32", PlatformID::Android32, pos), type); this->enable(this->addPlatformToggle("Android32", PlatformID::Android32), type);
this->enable(this->addPlatformToggle("Android64", PlatformID::Android64, pos), type); this->enable(this->addPlatformToggle("Android64", PlatformID::Android64), type);
m_platformsContainer->updateLayout();
// show installed // show installed
@ -51,26 +63,35 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
installedTitle->setScale(.5f); installedTitle->setScale(.5f);
m_mainLayer->addChild(installedTitle); m_mainLayer->addChild(installedTitle);
auto installedBG = CCScale9Sprite::create("square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f }); auto optionsContainerBG = CCScale9Sprite::create("square02b_001.png", { 0.0f, 0.0f, 80.0f, 80.0f });
installedBG->setColor({ 0, 0, 0 }); optionsContainerBG->setColor({ 0, 0, 0 });
installedBG->setOpacity(90); optionsContainerBG->setOpacity(90);
installedBG->setContentSize({ 290.f, 110.f }); optionsContainerBG->setContentSize({ 290.f, 110.f });
installedBG->setAnchorPoint({ 0.5f, 1.f }); optionsContainerBG->setAnchorPoint({ 0.5f, 1.f });
installedBG->setPosition(winSize.width / 2 - 85.f, winSize.height / 2 - 68.75f + iosAndAndroidSize - iosAndAndroidSize * 0.25f); optionsContainerBG->setPosition(winSize.width / 2 - 85.f, winSize.height / 2 - 68.75f + iosAndAndroidSize - iosAndAndroidSize * 0.25f);
installedBG->setScale(.5f); optionsContainerBG->setScale(.5f);
m_mainLayer->addChild(installedBG); m_mainLayer->addChild(optionsContainerBG);
pos = CCPoint { winSize.width / 2 - 140.f, winSize.height / 2 - 85.f + iosAndAndroidSize - iosAndAndroidSize * 0.25f }; m_optionsContainer = CCNode::create();
m_optionsContainer->setAnchorPoint({ 0.5f, 1.f });
m_optionsContainer->setContentSize(optionsContainerBG->getScaledContentSize());
m_optionsContainer->setPosition(optionsContainerBG->getPosition());
m_optionsContainer->setLayout(
ColumnLayout::create()
->setAxisReverse(true)
->setCrossAxisOverflow(true)
);
m_mainLayer->addChild(m_optionsContainer);
this->addToggle( this->addToggle(
"Show Installed", menu_selector(SearchFilterPopup::onShowInstalled), "Show Installed", menu_selector(SearchFilterPopup::onShowInstalled),
m_modLayer->getQuery().forceVisibility, 0, pos m_modLayer->getQuery().forceVisibility, 0, m_optionsContainer
); );
this->addToggle( this->addToggle(
"Show Invalid", menu_selector(SearchFilterPopup::onShowInvalid), "Show Invalid", menu_selector(SearchFilterPopup::onShowInvalid),
m_modLayer->getQuery().forceInvalid, 1, pos m_modLayer->getQuery().forceInvalid, 1, m_optionsContainer
); );
m_optionsContainer->updateLayout();
// tags // tags
@ -98,35 +119,35 @@ bool SearchFilterPopup::setup(ModListLayer* layer, ModListType type) {
tagsWrap->setPosition(tagsPos); tagsWrap->setPosition(tagsPos);
m_mainLayer->addChild(tagsWrap); m_mainLayer->addChild(tagsWrap);
auto tagsScroll = ScrollLayer::create(tagsSize / 2.f); m_tagLayer = ScrollLayer::create(tagsSize / 2.f);
tagsScroll->setTouchEnabled(true); m_tagLayer->setTouchEnabled(true);
tagsWrap->addChild(tagsScroll); m_tagLayer->m_contentLayer->setLayout(
ColumnLayout::create()
auto tagsMenu = CCMenu::create(); ->setCrossAxisOverflow(true)
tagsMenu->setPosition(.0f, .0f); ->setAutoGrowAxis(tagsSize.height / 2.f)
tagsMenu->setAnchorPoint({ .0f, .0f }); ->setAxisReverse(true)
tagsMenu->setContentSize(tagsSize / 2.f); );
tagsScroll->m_contentLayer->addChild(tagsMenu); tagsWrap->addChild(m_tagLayer);
// pos = CCPoint { winSize.width / 2 + 30.f, winSize.height / 2 + 45.f - iosAndAndroidSize * 0.25f };
pos = CCPoint { 0, tagsSize.height / 2 - 12.f };
for (auto& tag : Index::get()->getTags()) { for (auto& tag : Index::get()->getTags()) {
auto menu = CCMenu::create();
menu->setContentSize({ tagsSize.width / 2.f, 30.f });
auto toggle = CCMenuItemToggler::createWithStandardSprites( auto toggle = CCMenuItemToggler::createWithStandardSprites(
this, menu_selector(SearchFilterPopup::onTag), .5f this, menu_selector(SearchFilterPopup::onTag), .5f
); );
toggle->toggle(m_modLayer->getQuery().tags.count(tag)); toggle->toggle(m_modLayer->getQuery().tags.count(tag));
toggle->setPosition(pos.x + 12.f, pos.y); toggle->setPosition(12.f, 15.f);
toggle->setUserObject(CCString::create(tag)); toggle->setUserObject(CCString::create(tag));
tagsMenu->addChild(toggle); menu->addChild(toggle);
auto label = TagNode::create(tag); auto label = TagNode::create(tag);
label->setScale(.4f); label->setScale(.4f);
label->setAnchorPoint({ .0f, .5f }); label->setAnchorPoint({ .0f, .5f });
label->setPosition(pos.x + 22.f, pos.y); label->setPosition(22.f, 15.f);
tagsMenu->addChild(label); menu->addChild(label);
pos.y -= 22.5f; m_tagLayer->m_contentLayer->addChild(menu);
} }
return true; return true;
@ -166,23 +187,36 @@ void SearchFilterPopup::enable(CCMenuItemToggler* toggle, ModListType type) {
} }
CCMenuItemToggler* SearchFilterPopup::addToggle( CCMenuItemToggler* SearchFilterPopup::addToggle(
char const* title, SEL_MenuHandler selector, bool toggled, int tag, CCPoint& pos char const* title, SEL_MenuHandler selector,
bool toggled, int tag, CCNode* node
) { ) {
auto toggle = GameToolbox::createToggleButton( constexpr float HEIGHT = 30.f;
title, selector, toggled, m_buttonMenu, pos, this, m_mainLayer, .5f, .5f, 100.f,
{ 10.f, .0f }, "bigFont.fnt", false, tag, nullptr auto menu = CCMenu::create();
); menu->setContentSize({ node->getContentSize().width, HEIGHT });
auto toggle = CCMenuItemToggler::createWithStandardSprites(this, selector, .5f);
toggle->toggle(toggled);
toggle->setPosition(12.f, HEIGHT / 2);
toggle->setTag(tag); toggle->setTag(tag);
pos.y -= 22.5f; menu->addChild(toggle);
auto label = CCLabelBMFont::create(title, "bigFont.fnt");
label->limitLabelWidth(node->getContentSize().width - 30.f, .4f, .1f);
label->setAnchorPoint({ .0f, .5f });
label->setPosition(22.f, HEIGHT / 2);
menu->addChild(label);
node->addChild(menu);
return toggle; return toggle;
} }
CCMenuItemToggler* SearchFilterPopup::addPlatformToggle( CCMenuItemToggler* SearchFilterPopup::addPlatformToggle(const char* title, PlatformID id) {
char const* title, PlatformID id, CCPoint& pos
) {
return this->addToggle( return this->addToggle(
title, menu_selector(SearchFilterPopup::onPlatformToggle), title, menu_selector(SearchFilterPopup::onPlatformToggle),
m_modLayer->getQuery().platforms.count(id), id.to<int>(), pos m_modLayer->getQuery().platforms.count(id),
id.to<int>(), m_platformsContainer
); );
return nullptr; return nullptr;
} }

View file

@ -10,12 +10,13 @@ enum class ModListType;
class SearchFilterPopup : public Popup<ModListLayer*, ModListType> { class SearchFilterPopup : public Popup<ModListLayer*, ModListType> {
protected: protected:
ModListLayer* m_modLayer; ModListLayer* m_modLayer;
CCNode* m_platformsContainer;
CCNode* m_optionsContainer;
ScrollLayer* m_tagLayer;
bool setup(ModListLayer* layer, ModListType type) override; bool setup(ModListLayer* layer, ModListType type) override;
CCMenuItemToggler* addToggle( CCMenuItemToggler* addToggle(const char* title, SEL_MenuHandler selector, bool toggled, int tag, CCNode* target);
char const* title, SEL_MenuHandler selector, bool toggled, int tag, CCPoint& pos CCMenuItemToggler* addPlatformToggle(const char* title, PlatformID id);
);
CCMenuItemToggler* addPlatformToggle(char const* title, PlatformID id, CCPoint& pos);
void onPlatformToggle(CCObject*); void onPlatformToggle(CCObject*);
void onShowInstalled(CCObject*); void onShowInstalled(CCObject*);

View file

@ -306,7 +306,8 @@ public:
mz_zip_entry_close(m_handle); mz_zip_entry_close(m_handle);
GEODE_UNWRAP(file::writeBinary(dir / name, res)); GEODE_UNWRAP(file::createDirectoryAll(dir));
GEODE_UNWRAP(file::writeBinary(dir / name, res).expect("Unable to write to {}: {error}", dir / name));
return Ok(); return Ok();
} }
@ -338,10 +339,12 @@ public:
#endif #endif
if (m_entries.at(filePath).isDirectory) { if (m_entries.at(filePath).isDirectory) {
GEODE_UNWRAP(file::createDirectoryAll(dir / filePath)); GEODE_UNWRAP(file::createDirectoryAll(dir / filePath));
} else { }
else {
GEODE_UNWRAP(this->extractAt(dir, filePath)); GEODE_UNWRAP(this->extractAt(dir, filePath));
} }
} else { }
else {
log::error( log::error(
"Zip entry '{}' is not contained within zip bounds", "Zip entry '{}' is not contained within zip bounds",
dir / filePath dir / filePath